#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Datetime: 2020/4/7 10:58
# @Author  : CHEN Wang
# @Site    :
# @File    : indicators.py
# @Software: PyCharm

"""
脚本说明：
DataFrame 类
以下的函数都可以被直接add_func

https://ta-lib.org/function.html
https://www.bookstack.cn/read/talib-zh/funcs.md
"""

import numpy as np
import pandas as pd
try:
    import talib
except:
    print('PLEASE install TALIB to call these methods')
    pass
from quant_researcher.quant.factors.indicator_tools.base_series import REF, LLV, HHV, SMA, MA, EMA, DEMA, IF, IFLAST, ROLLMAX, MAX, ROLLMIN, MIN, SUM, CUMSUM, CUMPROD, ABS, IFAND, STD
from quant_researcher.quant.factors.factor_preprocess.preprocess import ts_threshold_signal


# %% 趋势线相关动量指标
def indicator_MA(DataFrame, period=30, **kwargs):
    """移动平均"""
    if period == 1:
        return None
    CLOSE = DataFrame['close']
    res = pd.DataFrame({'MA': MA(CLOSE, period)})
    return res


def indicator_EMA(DataFrame, period=30, **kwargs):
    """指数移动平均 Exponential Moving Average"""
    if period == 1:
        return None
    CLOSE = DataFrame['close']
    res = pd.DataFrame({'EMA': EMA(CLOSE, period)})
    return res


def indicator_SMA(DataFrame, period=30, weight=1, **kwargs):
    """
    简单移动平均：
    SMA = Cn*weight/period+(1-weight/period)*SMAn-1
    特殊情况：
    weight = 1 时， 等同于MA
    weight = 2 时， 等同于EMA  转换公式 weight = 2/(N+1), N为指数移动平均的期数
    """
    if period == 1:
        return None
    CLOSE = DataFrame['close']
    res = pd.DataFrame({'SMA': SMA(CLOSE, period, weight)})
    return res


def indicator_TRIMA(DataFrame, period=30, **kwargs):
    """
    TRIMA:Triangular Moving Average 三角移动平均线赋予价格系列的中间部分更大的权重。
    """
    if period == 1:
        return None
    res = talib.TRIMA(DataFrame.close.values, period)  # Triangular Moving Average
    res = pd.DataFrame({'TRIMA': res}, index=DataFrame.index)
    return res


def indicator_DEMA(DataFrame, period=30, **kwargs):
    if period == 1:
        return None
    res = talib.DEMA(DataFrame.close.values, period)  # Double Exponential Moving Average
    res = pd.DataFrame({'DEMA': res}, index=DataFrame.index)
    return res


def indicator_TEMA(DataFrame, period=30, **kwargs):
    if period == 1:
        return None
    res = talib.TEMA(DataFrame.close.values, period)  # TEMA - Triple Exponential Moving Average
    res = pd.DataFrame({'TEMA': res}, index=DataFrame.index)
    return res


def indicator_T3(DataFrame, period=5, **kwargs):
    if period == 1:
        return None
    res = talib.T3(DataFrame.close.values, period, vfactor=0)  # T3 - Triple Exponential Moving Average (T3)
    res = pd.DataFrame({'T3': res}, index=DataFrame.index)
    return res


def indicator_VWAP(DataFrame, period=120, **kwargs):
    """
    VWAP 以成交量为权重计算价格的加权平均。
    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    Typical = (HIGH + LOW + CLOSE) / 3
    MF = VOLUME * Typical
    VOLUME_SUM = SUM(VOLUME, period)
    MF_SUM = SUM(MF, period)
    VWAP = MF_SUM / VOLUME_SUM
    res = pd.DataFrame({'VWAP': VWAP})
    return res


def indicator_AMV(DataFrame, period=6, **kwargs):
    """
    AMV 指标用成交量作为权重对开盘价和收盘价的均值进行加权移动平均。
    成交量越大的价格对移动平均结果的影响越大，AMV 指标减小了成交量小的价格波动的影响。
    """
    OPEN = DataFrame.open
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    AMOV = VOLUME * (OPEN + CLOSE) / 2
    AMV = SUM(AMOV, period) / SUM(VOLUME, period)

    res = pd.DataFrame({'AMV': AMV})
    return res


def indicator_MCST():
    pass


def indicator_VIDYA(DataFrame, period=14, **kwargs):
    """
    VIDYA 也属于均线的一种，不同的是，VIDYA 的权值加入了 ER（EfficiencyRatio）指标。在当前趋势较强时，ER 值较大，VIDYA
    会赋予当前价格更大的权重，使得 VIDYA 紧随价格变动，减小其滞后性；在当前趋势较弱（比如振荡市中）,ER 值较小，VIDYA 会赋予
    当前价格较小的权重，增大 VIDYA 的滞后性，使其更加平滑，避免产生过多的交易信号。
    """
    if period == 1:
        return None
    CLOSE = DataFrame['close']
    VI = ABS(CLOSE - REF(CLOSE, period)) / SUM(ABS(CLOSE - REF(CLOSE, 1)), period)
    VIDYA = VI * CLOSE + (1 - VI) * REF(CLOSE, 1)
    VIDYA.name = 'VIDYA'
    res = pd.DataFrame(VIDYA)
    return res


def indicator_KAMA(DataFrame, period=30, **kwargs):
    """ KAMA - Kaufman Adaptive Moving Average"""
    if period == 1:
        return None
    res = talib.KAMA(DataFrame.close.values, period)  # Kaufman Adaptive Moving Average
    res = pd.DataFrame({'KAMA': res}, index=DataFrame.index)
    return res


def indicator_MAMA(DataFrame, **kwargs):
    """ MAMA - MESA Adaptive Moving Average"""
    mama, fama = talib.MAMA(DataFrame.close.values, fastlimit=0.5, slowlimit=0.05)  # MESA Adaptive Moving Average
    res = pd.DataFrame({'MAMA': mama}, index=DataFrame.index)
    return res


def indicator_HULLMA(DataFrame, period=10, **kwargs):
    """
    """
    if period in [1, 3]:
        return None
    CLOSE = DataFrame.close
    X = 2 * EMA(CLOSE, int(period / 2)) - EMA(CLOSE, period)
    HULLMA = EMA(X, int(np.sqrt(period)))
    res = pd.DataFrame({'HULLMA': HULLMA}, index=DataFrame.index)
    return res


def indicator_HMA(DataFrame, period=14, **kwargs):
    if period == 1:
        return None
    HIGH = DataFrame['high']
    res = pd.DataFrame({'HMA': MA(HIGH, period)})
    return res


def indicator_LMA(DataFrame, period=14, **kwargs):
    """
    LMA 为简单移动平均把收盘价替换为最低价。
    """
    if period == 1:
        return None
    LOW = DataFrame['low']
    res = pd.DataFrame({'LMA': MA(LOW, period)})
    return res


def indicator_IC(DataFrame, period=20, **kwargs):
    """
    在 IC 指标中，SPAN_A 与 SPAN_B 之间的部分称为云。如果价格在云上，则说明是上涨趋势（如果 SPAN_A>SPAN_B，则上涨趋势强烈；否则上涨趋势较弱）；
    如果价格在云下，则为下跌趋势（如果SPAN_A<SPAN_B，则下跌趋势强烈；否则下跌趋势较弱）。
    该指标的使用方式与移动平均线有许多相似之处，比如较快的线（TS）突破较慢的线（KS），价格突破 KS,价格突破云，SPAN_A 突破 SPAN_B等。
    我们产生信号的方式是：如果价格在云上方 SPAN_A>SPAN_B，则当价格上穿 KS 时买入；如果价格在云下方且 SPAN_A<SPAN_B，则当价格下穿 KS 时卖出。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']

    N1 = period
    N2 = 3 * period
    N3 = 6 * period

    TS = (ROLLMAX(HIGH, N1) + ROLLMIN(LOW, N1)) / 2
    KS = (ROLLMAX(HIGH, N2) + ROLLMIN(LOW, N2)) / 2
    SPAN_A = (TS + KS) / 2
    SPAN_B = (ROLLMAX(HIGH, N3) + ROLLMIN(LOW, N3)) / 2

    signal = CLOSE.copy()
    mask1 = (CLOSE > SPAN_A) & (CLOSE > SPAN_B) & (SPAN_A > SPAN_B)
    mask2 = (CLOSE < SPAN_A) & (CLOSE < SPAN_B) & (SPAN_A < SPAN_B)
    signal[mask1 & (CLOSE > KS)] = 'buy'
    signal[mask2 & (CLOSE < KS)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'IC'
    res = pd.DataFrame(signal)
    return res


def indicator_BBI(DataFrame, period=3, **kwargs):
    """BBI 是对不同时间长度的移动平均线取平均，能够综合不同移动平均线的平滑性和滞后性。如果收盘价上穿/下穿 BBI 则产生买入/卖出信号。"""
    N1 = 1 * period
    N2 = 2 * period
    N3 = 4 * period
    N4 = 8 * period
    C = DataFrame['close']
    bbi = (MA(C, N1) + MA(C, N2) + MA(C, N3) + MA(C, N4)) / 4
    res = pd.DataFrame({'BBI': bbi}, index=DataFrame.index)
    return res


def indicator_PBX(DataFrame, period=3, **kwargs):
    """
    瀑布线
    """
    if period == 1:
        return None

    C = DataFrame['close']
    PBX1 = (EMA(C, period) + EMA(C, 2 * period) + EMA(C, 4 * period)) / 3

    # N1 = 3
    # N2 = 5
    # N3 = 8
    # N4 = 13
    # N5 = 18
    # N6 = 24
    # PBX2 = (EMA(C, N2) + EMA(C, 2 * N2) + EMA(C, 4 * N2)) / 3
    # PBX3 = (EMA(C, N3) + EMA(C, 2 * N3) + EMA(C, 4 * N3)) / 3
    # PBX4 = (EMA(C, N4) + EMA(C, 2 * N4) + EMA(C, 4 * N4)) / 3
    # PBX5 = (EMA(C, N5) + EMA(C, 2 * N5) + EMA(C, 4 * N5)) / 3
    # PBX6 = (EMA(C, N6) + EMA(C, 2 * N6) + EMA(C, 4 * N6)) / 3
    DICT = {'PBX': PBX1}
    res = pd.DataFrame(DICT, index=DataFrame.index)
    return res


def indicator_MADisplaced(DataFrame, period=9, ma_period=26, **kwargs):
    """
    MADispaced 把一般的移动平均线 MA 向前移动 M 个交易日。用法与一般的移动平均线一样。
    如果收盘价上穿 MADisplaced，则产生买入信号；
    如果收盘价下穿 MADisplaced，则产生卖出信号。
    """
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    MA_CLOSE = MA(CLOSE, ma_period)
    MADisplaced = CLOSE - REF(MA_CLOSE, period)
    res = pd.DataFrame({'MADisplaced': MADisplaced})
    return res


def indicator_DMA(DataFrame, short_period=10, long_period=50, **kwargs):
    """
    平均线差 DMA
    """

    if short_period == 1:
        return None

    CLOSE = DataFrame.close
    DMA = MA(CLOSE, short_period) - MA(CLOSE, long_period)

    # DMA在talib里面命名叫APO
    # APO = talib.APO(DataFrame.close.values, fastperiod=short_period, slowperiod=long_period, matype=0)  # Absolute Price Oscillator

    res = pd.DataFrame({'DMA': DMA}, index=DataFrame.index)
    return res


def indicator_PPO(DataFrame, short_period=9, long_period=26, **kwargs):
    """PO（Price Oscillator）PO 指标求短期均线与长期均线之间的变化率。"""
    CLOSE = DataFrame['close']
    if short_period == 1:
        EMA_SHORT = CLOSE
    else:
        EMA_SHORT = EMA(CLOSE, short_period)
    EMA_LONG = EMA(CLOSE, long_period)
    PPO = (EMA_SHORT - EMA_LONG) / EMA_LONG * 100
    # PPO = talib.PPO(DataFrame.close.values, short_period, long_period, matype=0)  # Percentage Price Oscillator
    res = pd.DataFrame({'PPO': PPO}, index=DataFrame.index)
    return res


def indicator_PPO_revised(DataFrame, short_period=9, long_period=26, **kwargs):
    """PO（Price Oscillator）PO 指标求短期均线与长期均线之间的变化率。采用的普通移动平均"""
    CLOSE = DataFrame['close']
    if short_period == 1:
        MA_SHORT = CLOSE
    else:
        MA_SHORT = MA(CLOSE, short_period)
    MA_LONG = MA(CLOSE, long_period)
    PPO = (MA_SHORT - MA_LONG) / MA_LONG * 100
    res = pd.DataFrame({'PPO_revised': PPO}, index=DataFrame.index)
    return res


def indicator_MACD(DataFrame, short_period=12, long_period=26, ma_period=9, **kwargs):
    """
    MACD
    """
    if short_period == 1:
        return None

    CLOSE = DataFrame['close']
    DIF = EMA(CLOSE, short_period) - EMA(CLOSE, long_period)
    DEA = EMA(DIF, ma_period)
    MACD = (DIF - DEA) * 2

    res = pd.DataFrame({'MACD': MACD}, index=DataFrame.index)
    return res


def indicator_ZLMACD(DataFrame, short_period=12, long_period=26, **kwargs):
    """
    ZLMACD 指标是对 MACD 指标的改进，它在计算中使用 DEMA 而不是 EMA，可以克服 MACD 指标的滞后性问题。
    如果 ZLMACD 上穿/下穿 0，则产生买入/卖出信号。
    """
    if short_period == 1:
        return None

    CLOSE = DataFrame['close']
    ZLMACD = (2 * DEMA(CLOSE, short_period) - DEMA(DEMA(CLOSE, short_period), short_period)) - \
             (2 * DEMA(CLOSE, long_period) - DEMA(DEMA(CLOSE, long_period), long_period))
    res = pd.DataFrame({'ZLMACD': ZLMACD}, index=DataFrame.index)
    return res


def indicator_HLMA(DataFrame, period=10, **kwargs):
    """
    平均线差 HLMA
    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    HMA = MA(HIGH, period)
    LMA = MA(LOW, period)

    res1 = CLOSE - HMA
    res2 = CLOSE - LMA
    signal = CLOSE.copy()
    signal[(res1 > 0)] = 'buy'
    signal[(res2 < 0)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'HLMA'
    res = pd.DataFrame(signal)
    return res


def indicator_WC(DataFrame, short_period=10, long_period=50, **kwargs):
    """
    平均线差 WC
    WC 也可以用来代替收盘价构造一些技术指标（不过相对比较少用到）。我们这里用 WC 的短期均线和长期均线的交叉来产生交易信号。
    """
    if short_period == 1:
        return None
    HIGH = DataFrame.high
    LOW = DataFrame.low
    OPEN = DataFrame.open
    CLOSE = DataFrame.close
    WC = (HIGH + LOW + 2 * CLOSE) / 4
    EMA1 = EMA(WC, short_period)
    EMA2 = EMA(WC, long_period)

    WC = EMA1 - EMA2

    res = pd.DataFrame({'WC': WC}, index=DataFrame.index)
    return res


def indicator_ER(DataFrame, period=9, **kwargs):
    """
    ER（Elder Ray Index）
    ER 为动量指标。用来衡量市场的多空力量对比。
    在多头市场，人们会更贪婪地在接近高价的地方买入，BullPower 越高则当前多头力量越强；
    而在空头市场，人们可能因为恐惧而在接近低价的地方卖出。BearPower 越低则当前空头力量越强。
    当两者都大于 0 时，反映当前多头力量占据主导地位；
    两者都小于 0 则反映空头力量占据主导地位。
    如果 BearPower 上穿 0，则产生买入信号；
    如果 BullPower 下穿 0，则产生卖出信号。
    """

    if period == 1:
        return None
    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    BullPower = HIGH - EMA(CLOSE, period)
    BearPower = LOW - EMA(CLOSE, period)

    signal = CLOSE.copy()
    signal[(BullPower < 0) & (BearPower < 0)] = 'sell'
    signal[(BearPower > 0) & (BullPower > 0)] = 'buy'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'ER'

    res = pd.DataFrame(signal)
    return res


def indicator_OSC(DataFrame, period=20, **kwargs):
    """
    变动速率线
    震荡量指标OSC，也叫变动速率线。属于超买超卖类指标,是从移动平均线原理派生出来的一种分析指标。
    它反应当日收盘价与一段时间内平均收盘价的差离值,从而测出股价的震荡幅度。
    按照移动平均线原理，根据OSC的值可推断价格的趋势，如果远离平均线，就很可能向平均线回归。
    """
    CLOSE = DataFrame['close']
    OSC = (CLOSE - MA(CLOSE, period))
    DICT = {'OSC': OSC}

    res = pd.DataFrame(DICT)
    return res


def indicator_TII(DataFrame, period=9, **kwargs):
    """
    TII（Trend Intensity Index）
    TII 的计算方式与 RSI 相同，只是把其中的前后两天价格变化替换为价格与均线的差值。
    TII 可以用来反映价格的趋势以及趋势的强度。
    一般认为 TII>80(<20)时上涨（下跌）趋势强烈。
    TII 产生交易信号有几种不同的方法：
    上穿 20 买入，下穿 80 卖出（作为反转指标）；
    上穿 50 买入，下穿 50 卖出；

    上穿信号线买入，下穿信号线卖出。
    如果 TII 上穿 TII_SIGNAL，则产生买入信号；
    如果 TII 下穿 TII_SIGNAL，则产生卖出信号。

    """
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    CLOSE_MA = MA(CLOSE, period)
    DEV = CLOSE - CLOSE_MA
    DEVPOS = IF(DEV > 0, DEV, 0)
    DEVNEG = IF(DEV < 0, -DEV, 0)
    M = int(period / 2 + 1)
    SUMPOS = SUM(DEVPOS, M)
    SUMNEG = SUM(DEVNEG, M)
    TII = 100 * SUMPOS / (SUMPOS + SUMNEG)

    res = pd.DataFrame({'TII': TII})
    return res


def indicator_DPO(DataFrame, period=9, **kwargs):
    """
    DPO 是当前价格与延迟的移动平均线的差值，通过去除前一段时间的移动平均价格来减少长期的趋势对短期价格波动的影响。
    DPO>0，表明目前处于多头市场；
    DPO<0，表明目前处于空头市场。
    """
    if period == 1:
        return None
    CLOSE = DataFrame['close']
    DPO = CLOSE - REF(MA(CLOSE, period), int(period / 2 + 1))
    DICT = {'DPO': DPO}
    res = pd.DataFrame(DICT)
    return res


def indicator_BIAS(DataFrame, period=12, **kwargs):
    """
    乖离率
    """
    if period == 0:
        return None

    CLOSE = DataFrame['close']
    BIAS = (CLOSE - MA(CLOSE, period)) / MA(CLOSE, period) * 100
    res = pd.DataFrame({'BIAS': BIAS})
    return res


def indicator_MABIAS(DataFrame, period=6, **kwargs):
    """

    """
    C = DataFrame['close']
    MA1 = MA(C, period)
    MA2 = MA(C, 2 * period)
    MA3 = MA(C, 3 * period)
    MABIAS = (2 * MA2 - MA1 - MA3) / C * 100
    res = pd.DataFrame({'MABIAS': MABIAS})
    return res


def indicator_DBCD(DataFrame, period=12, ma_period=48, **kwargs):
    """
    DBCD（异同离差乖离率）为乖离率离差的移动平均。我们用 DBCD上穿 5/下穿-5来产生买入/卖出信号。
    """
    if (period == 1) or (ma_period == 1):
        return None

    CLOSE = DataFrame['close']
    BIAS = (CLOSE - MA(CLOSE, ma_period)) / MA(CLOSE, ma_period) * 100
    BIAS_DIFF = BIAS - REF(BIAS, period)
    DBCD = SMA(BIAS_DIFF, period, 1)
    res = pd.DataFrame({'DBCD': DBCD})
    return res


def indicator_SAR(DataFrame, acceleration=0, maximum=0, **kwargs):
    res = talib.SAR(DataFrame.high.values, DataFrame.low.values, acceleration, maximum)  # SAR - Parabolic SAR
    res = pd.DataFrame({'SAR': res}, index=DataFrame.index)
    return res


def indicator_SAREXT(DataFrame, startvalue=0, offsetonreverse=0, accelerationinitlong=0,
                     accelerationlong=0, accelerationmaxlong=0, accelerationinitshort=0, accelerationshort=0,
                     accelerationmaxshort=0, **kwargs):
    res = talib.SAREXT(DataFrame.high.values, DataFrame.low.values,
                       startvalue, offsetonreverse, accelerationinitlong, accelerationlong, accelerationmaxlong,
                       accelerationinitshort, accelerationshort, accelerationmaxshort)  # SAREXT - Parabolic SAR - Extended
    res = pd.DataFrame({'SAREXT': res}, index=DataFrame.index)
    return res


def indicator_FO(DataFrame, **kwargs):
    """
    在过去的 N 天内收盘价对序列[1,2,...,N]作回归得到回归直线，当收盘价超过回归直线的一定范围时买入，低过回归直线的一定范围时卖出。
    如果 REG 上穿 0.05/下穿-0.05 则产生买入/卖出信号。
    :param DataFrame:
    :return:
    """
    CLOSE = DataFrame['close']
    N = 40
    X = [1, 2, ..., N]
    Y = [REF(CLOSE, N - 1), ..., REF(CLOSE, 1), CLOSE]
    # 做回归得
    # REG_CLOSE = aX + b
    # REG = (CLOSE - REG_CLOSE) / REG_CLOSE
    return


# %% 收益类动量指标
def indicator_MTM(DataFrame, period=12, **kwargs):
    """
    动量线
    """
    C = DataFrame.close
    mom = C - REF(C, period)
    res = pd.DataFrame({'MTM': mom}, index=DataFrame.index)
    return res


def indicator_MICD(DataFrame, short_period=20, long_period=40, ma_period=6, **kwargs):
    """
    如果 MICD 上穿 0，则产生买入信号；
    如果 MICD 下穿 0，则产生卖出信号。
    """
    if short_period == 1:
        return None

    CLOSE = DataFrame['close']
    MI = CLOSE - REF(CLOSE, 1)
    MTMMA = SMA(MI, long_period, 1)
    DIF = MA(REF(MTMMA, 1), short_period) - MA(REF(MTMMA, 1), long_period)
    MICD = SMA(DIF, ma_period, 1)

    signal = MICD.copy()
    signal[(MICD > 0)] = 'buy'
    signal[(MICD < 0)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'MICD'

    res = pd.DataFrame(signal)
    return res


def indicator_Qstick(DataFrame, period=20, **kwargs):
    """
    Qstick 通过比较收盘价与开盘价来反映股价趋势的方向和强度。
    如果Qstick 上穿/下穿 0 则产生买入/卖出信号。
    """
    if period == 1:
        return None

    OPEN = DataFrame['open']
    CLOSE = DataFrame['close']
    Qstick = MA(CLOSE - OPEN, period)
    res = pd.DataFrame({'Qstick': Qstick})
    return res


def indicator_TRIX(DataFrame, period=14, **kwargs):
    """
    TRIX 趋向指标是一项超长周期的指标，波动频率较低，信号密度小，适用于进行长期控盘或投资。
    TRIX 求价格的三重指数移动平均的变化率。当 TRIX>0 时，当前可能处于上涨趋势；当 TRIX<0 时，当前可能处于下跌趋势。TRIX 相
    比于普通移动平均的优点在于它通过三重移动平均去除了一些小的趋势和市场的噪音。我们可以通过 TRIX 上穿/下穿 0 线产生买入/卖出信号。
    """
    if period == 1:
        return None

    # CLOSE = DataFrame.close
    # TRIPLE_EMA = EMA(EMA(EMA(CLOSE, period), period), period)
    # TRIX = (TRIPLE_EMA - REF(TRIPLE_EMA, 1)) / REF(TRIPLE_EMA, 1)

    TRIX = talib.TRIX(DataFrame.close.values, period)  # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA
    res = pd.DataFrame({'TRIX': TRIX}, index=DataFrame.index)

    return res


def indicator_COPP(DataFrame, short_period=12, long_period=24, ma_period=6, **kwargs):
    """
    COPP 指标用不同时间长度的价格变化率的加权移动平均值来衡量动量.
    """

    CLOSE = DataFrame['close']
    RC = 100 * ((CLOSE - REF(CLOSE, short_period)) / REF(CLOSE, short_period) + (CLOSE - REF(CLOSE, long_period)) / REF(CLOSE, long_period))
    COPP = EMA(RC, ma_period)
    res = pd.DataFrame({'COPP': COPP})
    return res


def indicator_KST(DataFrame, period=12, ma_period=6, **kwargs):
    """
    KST 结合了不同时间长度的 ROC 指标。如果 KST 上穿/下穿 0 则产生买入/卖出信号。
    """

    CLOSE = DataFrame['close']
    ROC_MA1 = MA((CLOSE - REF(CLOSE, period)) / REF(CLOSE, period), period)
    ROC_MA2 = MA((CLOSE - REF(CLOSE, int(1.5 * period))) / REF(CLOSE, int(1.5 * period)), period)
    ROC_MA3 = MA((CLOSE - REF(CLOSE, int(2 * period))) / REF(CLOSE, int(2 * period)), period)
    ROC_MA4 = MA((CLOSE - REF(CLOSE, int(3 * period))) / REF(CLOSE, int(3 * period)), period)
    KST_IND = (ROC_MA1 + ROC_MA2 * 2 + ROC_MA3 * 3 + ROC_MA4 * 4) / 10
    KST = MA(KST_IND, ma_period) * 100

    return pd.DataFrame({'KST': KST})


def indicator_POS(DataFrame, period=20, ma_period=60, **kwargs):
    """

    """
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    RET = (CLOSE - REF(CLOSE, period)) / REF(CLOSE, period)
    POS = (RET - LLV(RET, ma_period)) / (HHV(RET, ma_period) - LLV(RET, ma_period)) * 100
    res = pd.DataFrame({'POS': POS})
    return res


def indicator_RCCD(DataFrame, short_period=20, long_period=40, ma_period=6, **kwargs):
    """
    RC 指标为当前价格与昨日价格的比值。当 RC 指标>1 时，说明价格在上升；当 RC 指标增大时，说明价格上升速度在增快。
    当 RC 指标<1 时，说明价格在下降；当 RC 指标减小时，说明价格下降速度在增快。
    RCCD 指标先对 RC 指标进行平滑处理，再取不同时间长度的移动平均的差值，再取移动平均。如 RCCD 上穿/下穿 0 则产生买入/卖出信号。
    """
    if short_period == 1:
        return None

    CLOSE = DataFrame['close']
    RC = CLOSE / REF(CLOSE, ma_period)
    ARC1 = SMA(REF(RC, 1), ma_period, 1)
    DIF = MA(REF(ARC1, 1), short_period) - MA(REF(ARC1, 1), long_period)
    RCCD = SMA(DIF, ma_period, 1)

    signal = RCCD.copy()
    signal[(RCCD > 0)] = 'buy'
    signal[(RCCD < 0)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'RCCD'

    res = pd.DataFrame(signal)
    return res


def indicator_BOP(DataFrame, period=6, **kwargs):
    """
    BOP 的变化范围为-100 到 100，用来衡量收盘价与开盘价的距离（正、负距离）占最高价与最低价的距离的比例，反映了市场的多空力量对比。
    如果 BOP>0，则多头更占优势；BOP<0 则说明空头更占优势。
    BOP越大，则说明价格被往最高价的方向推动得越多；
    BOP 越小，则说明价格被往最低价的方向推动得越多我们可以用 BOP 上穿/下穿 0线来产生买入/卖出信号。

    """
    BOP = talib.BOP(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)  # Balance Of Power
    BOP = pd.DataFrame({'BOP': BOP}, index=DataFrame.index)
    res = pd.DataFrame(MA(BOP['BOP'], period) * 100)
    return res


def indicator_ADTM(DataFrame, period=23, **kwargs):
    """
    动态买卖气指标
    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    OPEN = DataFrame.open
    DTM = IF(OPEN > REF(OPEN, 1), MAX((HIGH - OPEN), (OPEN - REF(OPEN, 1))), 0)
    DBM = IF(OPEN < REF(OPEN, 1), MAX((OPEN - LOW), (OPEN - REF(OPEN, 1))), 0)
    STM = SUM(DTM, period)
    SBM = SUM(DBM, period)
    ADTM = IF(STM > SBM, (STM - SBM) / STM, IF(STM != SBM, (STM - SBM) / SBM, 0)) * 100
    res = pd.DataFrame({'ADTM': ADTM})
    return res


def indicator_DDI(DataFrame, period=13, **kwargs):
    """
    方向标准离差指数，分析DDI柱状线，由红变绿(正变负)，卖出信号参考；由绿变红，买入信号参考。
    """

    H = DataFrame['high']
    L = DataFrame['low']
    DMZ = IF((H + L) > (REF(H, 1) + REF(L, 1)), MAX(ABS(H - REF(H, 1)), ABS(L - REF(L, 1))), 0)
    DMF = IF((H + L) < (REF(H, 1) + REF(L, 1)), MAX(ABS(H - REF(H, 1)), ABS(L - REF(L, 1))), 0)
    DIZ = SUM(DMZ, period) / (SUM(DMZ, period) + SUM(DMF, period))
    DIF = SUM(DMF, period) / (SUM(DMF, period) + SUM(DMZ, period))
    DDI = (DIZ - DIF) * 100
    DICT = {'DDI': DDI}
    return pd.DataFrame(DICT)


def indicator_TSI(DataFrame, short_period=20, long_period=40, **kwargs):
    """
    TSI 是一种双重移动平均指标。与常用的移动平均指标对收盘价取移动平均不同，TSI 对两天收盘价的差值取移动平均。
    如果 TSI 上穿 10/下穿-10 则产生买入/卖出指标。
    """

    CLOSE = DataFrame['close']
    if short_period == 1:
        TSI = EMA(CLOSE - REF(CLOSE, 1), long_period) / EMA(ABS(CLOSE - REF(CLOSE, 1)), long_period) * 100
    else:
        TSI = EMA(EMA(CLOSE - REF(CLOSE, 1), long_period), short_period) / EMA(EMA(ABS(CLOSE - REF(CLOSE, 1)), long_period), short_period) * 100
    res = pd.DataFrame({'TSI': TSI})
    return res


def indicator_ARBR(DataFrame, period=26, **kwargs):
    if period == 1:
        return None

    AR_OR_BR = kwargs.pop('ARBR', 'AR')

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    OPEN = DataFrame.open
    AR = SUM(HIGH - OPEN, period) / SUM(OPEN - LOW, period) * 100
    BR = SUM(MAX(0, HIGH - REF(CLOSE, 1)), period) / SUM(MAX(0, REF(CLOSE, 1) - LOW), period) * 100
    if AR_OR_BR == 'AR':
        res = pd.DataFrame({'AR': AR})
    else:
        res = pd.DataFrame({'BR': BR})
    return res


def indicator_CR(DataFrame, period=26, **kwargs):
    """
    CR 与 AR、BR 类似。
    CR 通过比较最高价、最低价和典型价格来衡量市场人气，其衡量昨日典型价格在今日最高价、最低价之间的位置。
    CR 超过 200 时，表示股价上升强势；CR 低于 50 时，表示股价下跌强势。如果 CR 上穿 200/下穿 50 则产生买入/卖出信号。

    :param DataFrame:
    :param period:
    :return:
    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    TYP = (HIGH + LOW + CLOSE) / 3
    H = MAX(HIGH - REF(TYP, 1), 0)
    L = MAX(REF(TYP, 1) - LOW, 0)
    CR = SUM(H, period) / SUM(L, period) * 100

    # MA1 = REF(MA(CR, M1), M1 / 2.5 + 1)
    # MA2 = REF(MA(CR, M2), M2 / 2.5 + 1)
    # MA3 = REF(MA(CR, M3), M3 / 2.5 + 1)
    # return pd.DataFrame({'CR': CR, 'MA1': MA1, 'MA2': MA2, 'MA3': MA3})

    res = pd.DataFrame({'CR': CR})
    return res


def indicator_FISHER(DataFrame, period=20, **kwargs):
    """
    PRICE_CH 用来衡量当前价位于过去 N 天的最高价和最低价之间的位置。
    Fisher Transformation 是一个可以把股价数据变为类似于正态分布的方法。Fisher 指标的优点是减少了普通技术指标的滞后性。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    OPEN = DataFrame['open']
    CLOSE = DataFrame['close']
    PARAM = 0.3
    PRICE = (HIGH + LOW) / 2
    PRICE_CH = 2 * ((PRICE - LLV(LOW, period)) / (HHV(HIGH, period) - LLV(LOW, period)) - 0.5)
    # PRICE_CHANGE = 0.999 if PRICE_CH > 0.99
    # PRICE_CHANGE = -0.999 if PRICE_CH < -0.99
    PRICE_CH = PARAM * PRICE_CH + (1 - PARAM) * REF(PRICE_CH, 1)

    A = np.log10((1 + PRICE_CH) / (1 - PRICE_CH)) * 100
    FISHER = A.copy()
    FISHER.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    FISHER.dropna(inplace=True)  # 前面的NaN直接踢掉
    for i, idx in enumerate(FISHER.index):
        if i == 0:
            FISHER[idx] = 0.5 * A[idx]
        else:
            FISHER[idx] = 0.5 * FISHER.iloc[i] + 0.5 * A[idx]
    res = pd.DataFrame({'FISHER': FISHER})
    return res


def indicator_SMI(DataFrame, short_period=9, long_period=18, **kwargs):
    """
    SMI 指标可以看作 KDJ 指标的变形。
    不同的是，KD 指标衡量的是当天收盘价位于最近 N 天的最高价和最低价之间的位置，
    而 SMI 指标是衡量当天收盘价与最近 N 天的最高价与最低价均值之间的距离。
    我们用 SMI 指标上穿/下穿其均线产生买入/卖出信号。
    """
    if short_period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    M = (HHV(HIGH, short_period) + LLV(LOW, short_period)) / 2
    D = CLOSE - M
    DS = EMA(EMA(D, long_period), long_period)
    DHL = EMA(EMA(HHV(HIGH, short_period) - LLV(LOW, short_period), long_period), long_period)
    SMI = 100 * DS / DHL

    res = pd.DataFrame({'SMI': SMI})
    return res


def indicator_CLV(DataFrame, ma_period=20, **kwargs):
    """
    CLV 用来衡量收盘价在最低价和最高价之间的位置。
    当CLOSE=HIGH 时，CLV=1;
    当 CLOSE=LOW 时，CLV=-1;
    当 CLOSE位于 HIGH 和 LOW 的中点时，CLV=0。
    CLV>0（<0），说明收盘价离最高（低）价更近。我们用 CLVMA 上穿/下穿 0 来产生买入/卖出信号。
    """
    if ma_period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']

    CLV = (2 * CLOSE - LOW - HIGH) / (HIGH - LOW)  # todo 存在分母为0的情况，需要进行处理
    CLVMA = MA(CLV, ma_period) * 100
    res = pd.DataFrame({'CLV': CLVMA})
    return res


def indicator_KDJD(DataFrame, period=9, ma_period=30, **kwargs):
    """
    KDJD 可以看作 KDJ 的变形。
    KDJ 计算过程中的变量 Stochastics 用来衡量收盘价位于最近 N 天最高价和最低价之间的位置。
    而 KDJD 计算过程中的 Stochastics_DOUBLE 可以用来衡量 Stochastics 在最近N 天的 Stochastics 最大值与最小值之间的位置。
    我们这里将其用作动量指标。当 D 上穿 70/下穿 30 时，产生买入/卖出信号。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    LOW_N = LLV(LOW, period)
    HIGH_N = HHV(HIGH, period)
    Stochastics = (CLOSE - LOW_N) / (HIGH_N - LOW_N) * 100
    Stochastics_LOW = LLV(Stochastics, ma_period)
    Stochastics_HIGH = HHV(Stochastics, ma_period)
    Stochastics_DOUBLE = (Stochastics - Stochastics_LOW) / (Stochastics_HIGH - Stochastics_LOW) * 100
    K = SMA(Stochastics_DOUBLE, 3, 1)
    D = SMA(K, 3, 1)
    DICT = {'KDJD': D}
    res = pd.DataFrame(DICT)
    return res


def indicator_ASI(DataFrame, period=26, **kwargs):
    """
    由于 SI 的波动性比较大，所以我们一般对 SI 累计求和得到 ASI 并捕捉 ASI 的变化趋势。
    一般我们不会直接看 ASI 的数值（对 SI 累计求和的求和起点不同会导致求出 ASI 的值不同），而是会观察 ASI 的变化方向。
    我们利用 ASI 与其均线的交叉来产生交易信号,上穿/下穿均线时买入/卖出。
    """
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    OPEN = DataFrame['open']
    A = ABS(HIGH - REF(CLOSE, 1))
    B = ABS(LOW - REF(CLOSE, 1))
    C = ABS(HIGH - REF(LOW, 1))
    D = ABS(REF(CLOSE, 1) - REF(OPEN, 1))

    K = MAX(A, B)
    M = ROLLMAX(HIGH - LOW, period)
    R1 = A + 0.5 * B + 0.25 * D
    R2 = B + 0.5 * A + 0.25 * D
    R3 = C + 0.25 * D
    R4 = IF((A >= B) & (A >= C), R1, R2)
    R = IF((C >= A) & (C >= B), R3, R4)
    SI = 50 * (CLOSE - REF(CLOSE, 1) + (REF(CLOSE, 1) - REF(OPEN, 1)) + 0.5 * (CLOSE - OPEN)) / R * K / M
    ASI = CUMSUM(SI, 1)

    # 之前的算法
    # LC = REF(CLOSE, 1)
    # AA = ABS(HIGH - LC)
    # BB = ABS(LOW - LC)
    # CC = ABS(HIGH - REF(LOW, 1))
    # DD = ABS(LC - REF(OPEN, 1))
    # R = IFAND(AA > BB, AA > CC, AA + BB / 2 + DD / 4, IFAND(BB > CC, BB > AA, BB + AA / 2 + DD / 4, CC + DD / 4))
    # X = (CLOSE - REF(CLOSE, 1) + (CLOSE - OPEN) / 2 + REF(CLOSE, 1) - REF(OPEN, 1))
    # SI = 16 * X / R * MAX(AA, BB)
    # ASI = SUM(SI, period)
    res = pd.DataFrame({'ASI': ASI})
    return res


def indicator_PSY(DataFrame, period=20, **kwargs):
    """
    PSY 指标为过去 N 天股价上涨的天数的比例*100，用来衡量投资者心理和市场的人气。
    当 PSY 处于 40 和 60 之间时，多、空力量相对平衡，
    当 PSY 上穿 60 时，多头力量比较强，产生买入信号；
    当 PSY下穿 40 时，空头力量比较强，产生卖出信号。

    """
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    PSY = SUM(IF(CLOSE > REF(CLOSE, 1), 1, 0), period) / period * 100
    res = pd.DataFrame({'PSY': PSY})
    return res


def indicator_WAD(DataFrame, **kwargs):
    """
    我们用 WAD 上穿/下穿其均线来产生买入/卖出信号。

    """

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']

    TRH = MAX(HIGH, REF(CLOSE, 1))
    TRL = MIN(LOW, REF(CLOSE, 1))
    AD = IF(CLOSE > REF(CLOSE, 1), CLOSE - TRL, CLOSE - TRH)
    # AD = IF(CLOSE > REF(CLOSE, 1), 0, CLOSE - REF(CLOSE, 1))
    WAD = CUMSUM(AD)
    res = pd.DataFrame({'WAD': WAD})
    return res


def indicator_CV(DataFrame, period=20, **kwargs):
    """CV 指标用来衡量股价的波动，反映一段时间内最高价与最低价之差（价格变化幅度）的变化率。"""
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    H_L_EMA = EMA(HIGH - LOW, period)
    CV = (H_L_EMA - REF(H_L_EMA, period)) / REF(H_L_EMA, period) * 100
    res = pd.DataFrame({'CV': CV})
    return res


# %% 反转类指标
"""
反趋向指标:
主要捕捉趋势的转折点
随机指标KDJ
乖离率 BIAS
变动速率 ROC
顺势指标 CCI
威廉指标 WR
震荡量(变动速率) OSC
相对强弱指标 RSI
动态买卖指标 ADTM
"""
"""
压力支撑指标
主要用于分析股价目前收到的压力和支撑
布林带 BOLL
麦克指标 MIKE
震荡升降指标ASI
"""


def indicator_KDJ(DataFrame, period=9, **kwargs):
    if period == 1:
        return None

    # C = DataFrame['close']
    # H = DataFrame['high']
    # L = DataFrame['low']
    # RSV = ((C - LLV(L, period)) / (HHV(H, period) - LLV(L, period)) * 100).fillna(method='ffill')
    # K = SMA(RSV, 3)
    # D = SMA(K, 3)
    # J = 3 * K - 2 * D
    # DICT = {'KDJ': J}

    slowk, slowd = talib.STOCH(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, fastk_period=period, slowk_period=3, slowd_period=3, slowk_matype=0, slowd_matype=0)  # Stochastic
    res = pd.DataFrame({'STOCH_SLOWK': slowk, 'STOCH_SLOWD': slowd}, index=DataFrame.index)

    # fastk, fastd = talib.STOCHF(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, fastk_period=period, fastd_period=3, fastd_matype=0)  # Stochastic Fast
    # res = pd.DataFrame({'STOCHF_FASTK': fastk, 'STOCHF_FASTD': fastd}, index=DataFrame.index)

    J = 3 * res['STOCH_SLOWK'] - 2 * res['STOCH_SLOWD']
    J.name = 'KDJ'
    res = pd.DataFrame(J)
    return res


def indicator_SKDJ(DataFrame, period=9, ma_period=3, **kwargs):
    """SKDJ 为慢速随机波动（即慢速 KDJ）。SKDJ 中的 K 即 KDJ 中的 D，SKJ 中的 D 即 KDJ 中的 D 取移动平均。其用法与 KDJ 相同。"""
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    LOWV = LLV(DataFrame['low'], period)
    HIGHV = HHV(DataFrame['high'], period)
    RSV = (CLOSE - LOWV) / (HIGHV - LOWV) * 100
    MARSV = EMA(RSV, ma_period)
    K = MA(MARSV, ma_period)
    DICT = {'SKDJ': K}
    res = pd.DataFrame(DICT)
    return res


def indicator_UOS(DataFrame, period=12, **kwargs):
    """
    UOS 的用法与 RSI 指标类似，可以用来反映市场的超买超卖状态。
    一般来说，UOS 低于 30 市场处于超卖状态；UOS 高于 30 市场处于超买状态。
    如果 UOS 上穿 30，则产生买入信号；
    如果 UOS 下穿 70，则产生卖出信号。
    取值范围0 - 100
    """

    if period == 1:
        return None

    M = period
    N = 2 * period
    O = 4 * period

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    TH = MAX(HIGH, REF(CLOSE, 1))
    TL = MIN(LOW, REF(CLOSE, 1))
    TR = TH - TL
    XR = CLOSE - TL
    XRM = SUM(XR, M) / SUM(TR, M)
    XRN = SUM(XR, N) / SUM(TR, N)
    XRO = SUM(XR, O) / SUM(TR, O)
    UOS = 100 * (XRM * N * O + XRN * M * O + XRO * M * N) / (M * N + M * O + N * O)

    # timeperiod1 = 1 * period
    # timeperiod2 = 2 * period
    # timeperiod3 = 4 * period
    # res = talib.ULTOSC(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, timeperiod1, timeperiod2, timeperiod3)  # Ultimate Oscillator
    # res = pd.DataFrame({'ULTOSC': res}, index=DataFrame.index)
    res = pd.DataFrame({'UOS': UOS})
    return res


def indicator_WR(DataFrame, period=12, **kwargs):
    """
    威廉指标
    WR 指标事实上就是 100-KDJ 指标计算过程中的 Stochastics。WR指标用来衡量市场的强弱和超买超卖状态。
    """
    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    WR = 100 * (HHV(HIGH, period) - CLOSE) / (HHV(HIGH, period) - LLV(LOW, period))

    # WR = talib.WILLR(HIGH, LOW, CLOSE, period)  # Williams' %R
    res = pd.DataFrame({'WR': WR}, index=DataFrame.index)
    return res


def indicator_SO(DataFrame, period=12, **kwargs):
    pass


def indicator_RSI(DataFrame, period=12, **kwargs):
    """
    相对强弱指标 RSI1:SMA(MAX(CLOSE-LC,0),N1,1)/SMA(ABS(CLOSE-LC),N1,1)*100;
    RSI 反映一段时间内平均收益与平均亏损的对比。
    """

    if period == 1:
        return None

    # # 自己计算
    # CLOSE = DataFrame['close']
    # CLOSE_DIFF_POS = IF(CLOSE > REF(CLOSE, 1), CLOSE - REF(CLOSE, 1), 0)
    # RSI = SMA(CLOSE_DIFF_POS, period, 1) / SMA(ABS(CLOSE - REF(CLOSE, 1)), period, 1) * 100

    # TALIB计算
    RSI = talib.RSI(DataFrame.close.values, period)  # Relative Strength Index
    res = pd.DataFrame({'RSI': RSI}, index=DataFrame.index)

    return res


def indicator_IMI(DataFrame, period=12, **kwargs):
    """
    IMI 的计算方法与 RSI 很相似。其区别在于，在 IMI 计算过程中使用的是收盘价和开盘价，而 RSI 使用的是收盘价和前一天的收盘价。
    所以，RSI 做的是前后两天的比较，而 IMI 做的是同一个交易日内的比较。
    如果 IMI 上穿 80，则产生买入信号；如果 IMI 下穿 20，则产生卖出信号。
    取值范围0 - 100
    """

    if period == 1:
        return None

    CLOSE = DataFrame['close']
    OPEN = DataFrame['open']
    INC = SUM(IF(CLOSE > OPEN, CLOSE - OPEN, 0), period)
    DEC = SUM(IF(OPEN > CLOSE, OPEN - CLOSE, 0), period)
    IMI = INC / (INC + DEC) * 100
    res = pd.DataFrame({'IMI': IMI})
    return res


def indicator_DMI(DataFrame, period=14, **kwargs):
    """
    趋向指标 DMI
    """
    if period == 1:
        return None

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    OPEN = DataFrame.open

    TR = SUM(MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1))), period)
    HD = HIGH - REF(HIGH, 1)
    LD = REF(LOW, 1) - LOW
    DMP = SUM(IFAND(HD > 0, HD > LD, HD, 0), period)
    DMM = SUM(IFAND(LD > 0, LD > HD, LD, 0), period)
    DI1 = DMP * 100 / TR
    DI2 = DMM * 100 / TR
    # ADX = MA(ABS(DI2 - DI1) / (DI1 + DI2) * 100, M2)
    # ADXR = (ADX + REF(ADX, M2)) / 2

    if period == 1:
        return None
    res = talib.DX(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)  # Directional Movement Index
    res = pd.DataFrame({'DX': res}, index=DataFrame.index)
    res = pd.DataFrame({'DI': DI1 - DI2})
    return res


def indicator_RMI(DataFrame, period=12, **kwargs):
    """
    RMI 与 RSI 的计算方式类似，
    将 RSI 中的动量与前一天价格之差CLOSE-REF(CLOSE,1)项改为了与前四天价格之差 CLOSE-REF(CLOSE,4)
    取值范围0 -100
    """

    if period == 1:
        return None

    CLOSE = DataFrame['close']
    RMI = SMA(MAX(CLOSE - REF(CLOSE, 4), 0), period, 1) / SMA(ABS(CLOSE - REF(CLOSE, 4)), period, 1) * 100
    res = pd.DataFrame({'RMI': RMI})
    return res


def indicator_DZRSI(DataFrame, period=14, ma_period=6, **kwargs):
    """
    DZRSI 为动态的 RSI，原理与 DZCCI 相同，计算公式为 DZCCI 中的CCI 替换为 RSI。
    """
    if period == 1:
        return None

    res = talib.RSI(DataFrame.close.values, period)
    RSI = pd.Series(res, index=DataFrame.index)

    PARAM = 2
    RSI_MIDDLE = MA(RSI, period)
    RSI_UPPER = RSI_MIDDLE + PARAM * STD(RSI, period)
    RSI_LOWER = RSI_MIDDLE - PARAM * STD(RSI, period)
    RSI_MA = MA(RSI, ma_period)

    signal = DataFrame.close.copy()
    signal[(RSI_MA > RSI_UPPER)] = 'buy'
    signal[(RSI_MA < RSI_LOWER)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'DZRSI'

    res = pd.DataFrame(signal)
    return res


def indicator_RSIS(DataFrame, period=12, ma_period=6, **kwargs):
    """
    RSIS 反映当前 RSI 在最近 N 天的 RSI 最大值和最小值之间的位置，
    与 KDJ 指标的构造思想类似。由于 RSIS 波动性比较大，我们先取移
    动平均再用其产生信号。其用法与 RSI 指标的用法类似。
    RSISMA 上穿 40 则产生买入信号；
    RSISMA 下穿 60 则产生卖出信号
    """

    if period == 1:
        return None

    CLOSE = DataFrame['close']
    CLOSE_DIFF_POS = IF(CLOSE > REF(CLOSE, 1), CLOSE - REF(CLOSE, 1), 0)
    RSI = SMA(CLOSE_DIFF_POS, period, 1) / SMA(ABS(CLOSE - REF(CLOSE, 1)), period, 1) * 100
    RSIS = (RSI - LLV(RSI, period)) / (HHV(RSI, period) - LLV(RSI, period)) * 100
    RSISMA = EMA(RSIS, ma_period)
    res = pd.DataFrame({'RSIS': RSISMA})
    return res


def indicator_DO(DataFrame, period=12, ma_period=6, **kwargs):
    """
    DO 是平滑处理（双重移动平均）后的 RSI 指标。DO 大于 0 则说明市场处于上涨趋势，小于 0 说明市场处于下跌趋势。
    我们用 DO 上穿/下穿其移动平均线来产生买入/卖出信号。
    """

    if period == 1:
        return None

    res = talib.RSI(DataFrame.close.values, period)
    RSI = pd.Series(res, index=DataFrame.index)
    DO = EMA(EMA(RSI, period), ma_period)
    DOMA = MA(DO, ma_period)

    signal = DataFrame.close.copy()
    signal[(DO - DOMA > 0)] = 'buy'
    signal[(DO - DOMA < 0)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'DO'
    res = pd.DataFrame(signal)
    return res


def indicator_ROC(DataFrame, period=12, **kwargs):
    """
    变动率指标
    ROC 衡量价格的涨跌幅。ROC 可以用来反映市场的超买超卖状态。
    当 ROC 过高时，市场处于超买状态；当 ROC 过低时，市场处于超卖状态。这些情况下，可能会发生反转。
    如果 ROC 上穿 5%，则产生买入信号；
    如果 ROC 下穿-5%，则产生卖出信号。
    """

    if period == 1:
        return None

    # 自己计算
    # CLOSE = DataFrame['close']
    # ROC = 100 * (CLOSE - REF(CLOSE, N)) / REF(CLOSE, N)

    # TALIB计算
    ROC = talib.ROC(DataFrame.close.values, period)  # Rate of change : ((price/prevPrice)-1)*100
    res = pd.DataFrame({'ROC': ROC}, index=DataFrame.index)

    return res


def indicator_SROC(DataFrame, period=12, ma_period=6, **kwargs):
    """
    SROC 与 ROC 类似，但是会对收盘价进行平滑处理后再求变化率。
    """

    CLOSE = DataFrame['close']
    EMAP = EMA(CLOSE, ma_period)
    SROC = (EMAP - REF(EMAP, period)) / REF(EMAP, period) * 100
    DICT = {'SROC': SROC}
    res = pd.DataFrame(DICT)
    return res


def indicator_PMO(DataFrame, **kwargs):
    """
    PMO 指标是 ROC 指标的双重平滑（移动平均）版本。与 SROC 不
    同(SROC 是先对价格作平滑再求 ROC)，而 PMO 是先求 ROC 再对
    ROC 作平滑处理。PMO 越大（大于 0），则说明市场上涨趋势越强；
    PMO 越小（小于 0），则说明市场下跌趋势越强。如果 PMO 上穿/
    下穿其信号线，则产生买入/卖出指标。

    :param DataFrame:
    :return:
    """
    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']

    N1 = 10
    N2 = 40
    N3 = 20
    ROC = (CLOSE - REF(CLOSE, 1)) / REF(CLOSE, 1) * 100
    # ROC_MA = DMA(ROC, 2 / N1)
    # ROC_MA10 = ROC_MA * 10
    # PMO = DMA(ROC_MA10, 2 / N2)
    # PMO_SIGNAL = DMA(PMO, 2 / (N3 + 1))

    return


def indicator_CCI(DataFrame, period=14, **kwargs):
    """
    TYP:=(HIGH+LOW+CLOSE)/3;
    CCI:(TYP-MA(TYP,N))/(0.015*AVEDEV(TYP,N));

    CCI 指标用来衡量典型价格（最高价、最低价和收盘价的均值）与其一段时间的移动平均的偏离程度。
    CCI 可以用来反映市场的超买超卖状态。一般认为，CCI 超过 100 则市场处于超买状态；CCI 低于-100则市场处于超卖状态。
    当 CCI 下穿 100/上穿-100 时，说明股价可能要开始发生反转，可以考虑卖出/买入。
    """

    if period == 1:
        return None

    # 自己计算
    # typ = (DataFrame['high'] + DataFrame['low'] + DataFrame['close']) / 3
    # ## 此处AVEDEV可能为0值  因此导致出错 +0.0000000000001
    # cci = ((typ - MA(typ, N)) / (0.015 * AVEDEV(typ, N) + 0.00000001))
    # a = 100
    # b = -100
    # res = pd.DataFrame({'CCI': cci, 'a': a, 'b': b})

    # TALIB计算
    res = talib.CCI(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)  # Commodity Channel Index
    res = pd.DataFrame({'CCI': res}, index=DataFrame.index)

    return res


def indicator_DZCCI(DataFrame, period=14, ma_period=6, **kwargs):
    """
    一般来说，CCI 产生交易信号的阈值可以由 100/-100 来确定，但是在市场处于不同状况时最优的阈值可能不同。
    DZCCI 根据前段时间CCI 的变化来动态地确定阈值，从而产生交易信号。
    DZCCI 的构造方法类似于布林带，用 CCI 的长期移动平均线加减常数倍标准差得到。
    我们用CCI的短期移动平均线上穿/下穿DZCCI的上/下轨来产生买入/卖出信号。
    """
    if period == 1:
        return None
    res = talib.CCI(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)
    CCI = pd.Series(res, index=DataFrame.index)

    PARAM = 2
    CCI_MIDDLE = MA(CCI, period)
    CCI_UPPER = CCI_MIDDLE + PARAM * STD(CCI, period)
    CCI_LOWER = CCI_MIDDLE - PARAM * STD(CCI, period)
    CCI_MA = MA(CCI, ma_period)

    signal = DataFrame.close.copy()
    signal[(CCI_MA > CCI_UPPER)] = 'buy'
    signal[(CCI_MA < CCI_LOWER)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'DZCCI'

    res = pd.DataFrame(signal)
    return res


def indicator_DEM(DataFrame, period=20, **kwargs):
    """
    当 Demaker>0.7 时上升趋势强烈，当 Demaker<0.3 时下跌趋势强烈。当 Demaker 上穿 0.7/下穿 0.3 时产生买入/卖出信号。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    Demax = HIGH - REF(HIGH, 1)
    Demax = IF(Demax > 0, Demax, 0)
    Demin = REF(LOW, 1) - LOW
    Demin = IF(Demin > 0, Demin, 0)
    Demaker = MA(Demax, period) / (MA(Demax, period) + MA(Demin, period)) * 100
    res = pd.DataFrame({'Demaker': Demaker})
    return res


def indicator_STC(DataFrame, short_period=12, long_period=24, ma_period=6, **kwargs):
    """
    STC 指标结合了 MACD 指标和 KDJ 指标的算法。首先用短期均线与长期均线之差算出 MACD，再求 MACD 的随机快速随机指标 FK 和FD，
    最后求 MACD 的慢速随机指标 SK 和 SD。其中慢速随机指标就是 STC 指标。
    STC 指标可以用来反映市场的超买超卖状态。一般认为 STC 指标超过 75 为超买，STC 指标低于 25 为超卖。
    """
    if short_period == 1:
        return None
    CLOSE = DataFrame['close']
    MACDX = EMA(CLOSE, short_period) - EMA(CLOSE, long_period)
    V1 = ROLLMIN(MACDX, ma_period)
    V2 = ROLLMAX(MACDX, ma_period) - V1

    FK = IFLAST(V2 > 0, (MACDX - V1) / V2 * 100)
    FD = SMA(FK, ma_period, 1)
    V3 = ROLLMIN(FD, ma_period)
    V4 = ROLLMAX(FD, ma_period) - V3
    SK = IFLAST(V4 > 0, (FD - V3) / V4 * 100)
    STC = SMA(SK, ma_period, 1)
    res = pd.DataFrame({'STC': STC})
    return res


def indicator_RVI(DataFrame, period=12, ma_period=6, **kwargs):
    """
    RVI 的计算方式与 RSI 一样，
    不同的是将 RSI 计算中的收盘价变化值替换为收盘价在过去一段时间的标准差，用来反映一段时间内上升的波动率和下降的波动率的对比。
    我们也可以像计算 RSI 指标时一样
    先对公式中的 USTD 和 DSTD 作移动平均得到 USTD_MA 和DSTD_MA 再求出 RVI=100*USTD_MV/(USTD_MV+DSTD_MV)。
    """

    if period == 1:
        return None

    CLOSE = DataFrame['close']
    STD1 = STD(CLOSE, period)
    USTD = SUM(IF(CLOSE > REF(CLOSE, 1), STD1, 0), ma_period)
    DSTD = SUM(IF(CLOSE < REF(CLOSE, 1), STD1, 0), ma_period)
    RVI = 100 * USTD / (USTD + DSTD)
    res = pd.DataFrame({'RVI': RVI})
    return res


def indicator_Inertia(DataFrame, period=12, **kwargs):
    pass


def indicator_ENV(DataFrame, period=20, **kwargs):
    """
    ENV（Envolope 包络线）指标是由移动平均线上下平移一定的幅度（百分比）所得。
    我们知道，价格与移动平均线的交叉可以产生交易信号。但是因为市场本身波动性比较强，可能产生很多虚假的交易信号。
    所以我们把移动平均线往上往下平移。当价格突破上轨时再产生买入信号或者当价格突破下轨再产生卖出信号。
    这样的方式可以去掉很多假信号

    在指标BIAS里面已经测试过
    """
    CLOSE = DataFrame['close']
    PARAM = 0.1
    MAC = MA(CLOSE, period)
    UPPER = MAC * (1 + PARAM)
    LOWER = MAC * (1 - PARAM)

    res1 = CLOSE - UPPER
    res2 = CLOSE - LOWER
    signal = CLOSE.copy()
    signal[(res1 > 0)] = 'buy'
    signal[(res2 < 0)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'ENV'

    res = pd.DataFrame(signal)
    return res


def indicator_HLC(DataFrame, period=20, **kwargs):
    """
    DC 指标用 N 天最高价和 N 天最低价来构造价格变化的上轨和下轨，
    再取其均值作为中轨。当收盘价上穿/下穿中轨时产生买入/卖出信号。
    """

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    UPPER = HHV(HIGH, period)
    LOWER = LLV(LOW, period)
    MIDDLE = (UPPER + LOWER) / 2
    return pd.DataFrame({'DC': MIDDLE})


def indicator_PAC(DataFrame, period=20, **kwargs):
    """
    用最高价和最低价的移动平均来构造价格变化的通道，如果价格突破上轨则做多，突破下轨则做空。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    UPPER = CLOSE - MA(HIGH, period)
    LOWER = CLOSE - MA(LOW, period)

    signal = CLOSE.copy()
    signal[(UPPER > 0)] = 'buy'
    signal[(LOWER < 0)] = 'sell'
    signal[(signal != 'sell') & (signal != 'buy')] = np.nan
    signal.fillna(method='ffill', inplace=True)  # 后面的NaN用前值填充
    signal.dropna(inplace=True)  # 前面的NaN直接踢掉
    signal[signal == 'buy'] = 1
    signal[signal == 'sell'] = 0
    signal.name = 'PAC'

    res = pd.DataFrame(signal)
    return res


def indicator_BOLL(DataFrame, period=20, **kwargs):
    """
    布林线
    """
    CLOSE = DataFrame['close']
    ma = MA(CLOSE, period)
    # UB = boll + P * STD(C, N)
    # LB = boll - P * STD(C, N)
    # DICT = {'BOLL': ma, 'UB': UB, 'LB': LB}

    # upperband, middleband, lowerband = talib.BBANDS(DataFrame.close.values, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)  # Bollinger Bands

    boll_ratio = (CLOSE - ma) / STD(CLOSE, period) * 100
    res = pd.DataFrame({'BOLL': boll_ratio})
    return res


def indicator_FB(DataFrame, period=20, **kwargs):
    """
    FB 指标类似于布林带，都以价格的移动平均线为中轨，在中线上下浮动一定数值构造上下轨。
    不同的是，Fibonacci Bands 有三条上轨和三条下轨，且分别为中轨加减 ATR 乘 Fibonacci 因子所得。
    当收盘价突破较高的两个上轨的其中之一时，产生买入信号；收盘价突破较低的两个下轨的其中之一时，产生卖出信号。

    indicator_KC 的特殊使用场景
    """

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    TR = MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1)))
    ATR = MA(TR, period)
    MIDDLE = MA(CLOSE, period)
    UPPER1 = MIDDLE + 1.618 * ATR
    UPPER2 = MIDDLE + 2.618 * ATR
    UPPER3 = MIDDLE + 4.236 * ATR
    LOWER1 = MIDDLE - 1.618 * ATR
    LOWER2 = MIDDLE - 2.618 * ATR
    LOWER3 = MIDDLE - 4.236 * ATR

    res1 = CLOSE - UPPER2
    res2 = CLOSE - LOWER2
    res = pd.DataFrame()
    return res


def indicator_KC(DataFrame, period=20, **kwargs):
    """
    KC 指标（KeltnerChannel）与布林带类似，都是用价格的移动平均构造中轨，不同的是表示波幅的方法，这里用 ATR 来作为波幅构造上下轨。
    价格突破上轨，可看成新的上升趋势，买入；价格突破下轨， 可看成新的下降趋势，卖出。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    TR = MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1)))
    ATR = MA(TR, period)
    MIDDLE = EMA(CLOSE, period)
    UPPER = MIDDLE + 2 * ATR
    LOWER = MIDDLE - 2 * ATR

    KC_ratio = (CLOSE - MIDDLE) / ATR * 100
    res = pd.DataFrame({'KC': KC_ratio})
    return res


def indicator_APZ(DataFrame, period=20, **kwargs):
    """
    APZ（Adaptive Price Zone 自适应性价格区间）与布林线 BollingerBand 和肯通纳通道 Keltner Channel 很相似，
    都是根据价格波动性围绕均线而制成的价格通道。
    只是在这三个指标中计算价格波动性的方法不同。在布林线中用了收盘价的标准差，在肯通纳通道中用了真波幅 ATR，
    而在 APZ 中运用了最高价与最低价差值的 N 日双重指数平均来反映价格的波动幅度。
    """
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    VOL = EMA(EMA(HIGH - LOW, period), period)
    MIDDLE = EMA(EMA(CLOSE, period), period)
    UPPER = MIDDLE + 2 * VOL
    LOWER = MIDDLE - 2 * VOL

    APZ = (CLOSE - MIDDLE) / VOL * 100
    res = pd.DataFrame({'APZ': APZ})
    return res


def indicator_MI(DataFrame, period=14, **kwargs):
    """ Mass Index"""
    if period == 1:
        return None

    HIGH = DataFrame['high']
    LOW = DataFrame['low']

    ratio = EMA(HIGH - LOW, 9) / EMA(EMA(HIGH - LOW, 9), 9)
    MI = SUM(ratio, period) / period
    res = pd.DataFrame({'MI': MI})
    return res


# %% 市场运行状态类指标
def indicator_AROON(DataFrame, period=14, **kwargs):
    """
    阿隆指标
    AroonOsc = AroonUp - AroonDown

    ArronUp、ArronDown 指标分别为考虑的时间段内最高价、最低价出
    现时间与当前时间的距离占时间段长度的百分比。如果价格当天创新
    高，则 ArronUp 等于 100；创新低，则 ArronDown 等于 100。Arron
    指标为两者之差，变化范围为-100 到 100。Arron 指标大于 0 表示股
    价呈上升趋势，Arron 指标小于 0 表示股价呈下降趋势。距离 0 点越
    远则趋势越强。我们这里以 20/-20 为阈值构造交易信号。如果
    ArronOs 上穿 20/下穿-20 则产生买入/卖出信号。

    """

    if period == 1:
        return None

    # ar_up, ar_down = talib.AROON(DataFrame.high.values, DataFrame.low.values, period)  # Aroon
    # res = pd.DataFrame({'AROON_UP': ar_up, 'AROON_DOWN': ar_down}, index=DataFrame.index)

    AROONOSC = talib.AROONOSC(DataFrame.high.values, DataFrame.low.values, period)  # Aroon Oscillator
    res = pd.DataFrame({'AROONOSC': AROONOSC}, index=DataFrame.index)

    return res


def indicator_CMO(DataFrame, period=14, **kwargs):
    """
    指标介绍
    CMO(Chande Momentum Oscillator)动量震荡指标是由Tushar S. Chande提出的类似于RSI的指标
    CMOn是一个n天滚动指标，在这n天中的第i天计算每天的 收盘价 - 前收盘价 ，如果为正则赋给upi（dni为0），为负则将绝对值赋给dni（upi为0）
    """
    if period == 1:
        return None

    # SU = SUM(MAX(CLOSE - REF(CLOSE, 1), 0), N)
    # SD = SUM(MAX(REF(CLOSE, 1) - CLOSE, 0), N)
    # CMO = (SU - SD) / (SU + SD) * 100

    CMO = talib.CMO(DataFrame.close.values, period)  # Chande Momentum Oscillator
    res = pd.DataFrame({'CMO': CMO}, index=DataFrame.index)

    return res


def indicator_ADX(DataFrame, period=14, **kwargs):
    """
    ADX 指标计算过程中的 DI+与 DI-指标用相邻两天的最高价之差与最低价之差来反映价格的变化趋势。当 DI+上穿 DI-时，产生买入信号；
    当 DI+下穿 DI-时，产生卖出信号。
    :param DataFrame:
    :param period:
    :return:
    """
    if period == 1:
        return None

    # 自己计算
    # HIGH = DataFrame.high
    # LOW = DataFrame.low
    # CLOSE = DataFrame.close
    # MAX_HIGH = IF(HIGH > REF(HIGH, 1), HIGH - REF(HIGH, 1), 0)
    # MAX_LOW = IF(REF(LOW, 1) > LOW, REF(LOW, 1) - LOW, 0)
    # XPDM = IF(MAX_HIGH > MAX_LOW, HIGH - REF(HIGH, 1), 0)
    # PDM = SUM(XPDM, period)
    # XNDM = IF(MAX_LOW > MAX_HIGH, REF(LOW, 1) - LOW, 0)
    # NDM = SUM(XNDM, period)
    # TR = MAX(MAX(ABS(HIGH - LOW), ABS(HIGH - CLOSE)), ABS(LOW - CLOSE))
    # TR = SUM(TR, period)
    # DI_pos = PDM / TR
    # DI_neg = NDM / TR
    # ADX = MA(ABS(DI_pos - DI_neg) / (DI_pos + DI_neg) * 100, period)
    # ADXR = (ADX + REF(ADX, period)) / 2

    ADX = talib.ADX(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)  # Average Directional Movement Index
    res = pd.DataFrame({'ADX': ADX}, index=DataFrame.index)

    return res


def indicator_ADXR(DataFrame, period=14, **kwargs):
    """
    ADXR 是 ADX 指标与 N 天前的 ADX 指标的均值。用 ADX 上穿/下穿ADXR 来产生买入/卖出信号。
    ADXR=(ADX+REF(ADX,N))/2
    :param DataFrame:
    :param period:
    :return:
    """
    if period == 1:
        return None

    ADXR = talib.ADXR(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)  # Average Directional Movement Index Rating
    res = pd.DataFrame({'ADXR': ADXR}, index=DataFrame.index)
    return res


def indicator_CSI(DataFrame, period=14, **kwargs):
    pass


def indicator_R_Squared(DataFrame, period=14, **kwargs):
    import statsmodels.api as sm


    def inner_regression(datay):
        X = sm.add_constant(pd.Series(np.arange(period)))
        X.index = datay.index
        model_result = sm.OLS(datay, X, missing='drop').fit()
        return round(model_result.rsquared_adj, 2)

    CLOSE = DataFrame.close
    R_Squared = CLOSE.rolling(period).apply(inner_regression)

    res = pd.DataFrame({'R_Squared': R_Squared}, index=DataFrame.index)
    return res


def indicator_RWI_H(DataFrame, period=14, **kwargs):
    """
    RWI（随机漫步指标）对一段时间股票的随机漫步区间与真实运动区间进行比较以判断股票价格的走势。
    如果 RWIH>1，说明股价长期是上涨趋势，则产生买入信号；
    如果 RWIL>1，说明股价长期是下跌趋势，则产生卖出信号。
    """
    if period == 1:
        return None

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    TR = MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1)))
    ATR = MA(TR, period)
    RWIH = (HIGH - REF(LOW, 1)) / (ATR * np.sqrt(period))
    # RWIL = (REF(HIGH, 1) - LOW) / (ATR * np.sqrt(period))
    res = pd.DataFrame({'RWIH': RWIH}, index=DataFrame.index)
    return res


def indicator_RWI_L(DataFrame, period=14, **kwargs):
    """
    RWI（随机漫步指标）对一段时间股票的随机漫步区间与真实运动区间进行比较以判断股票价格的走势。
    如果 RWIH>1，说明股价长期是上涨趋势，则产生买入信号；
    如果 RWIL>1，说明股价长期是下跌趋势，则产生卖出信号。
    """
    if period == 1:
        return None

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    TR = MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1)))
    ATR = MA(TR, period)
    # RWIH = (HIGH - REF(LOW, 1)) / (ATR * np.sqrt(period))
    RWIL = (REF(HIGH, 1) - LOW) / (ATR * np.sqrt(period))
    res = pd.DataFrame({'RWIL': RWIL}, index=DataFrame.index)
    return res


def indicator_VHF(DataFrame, period=14, **kwargs):
    if period == 1:
        return None

    CLOSE = DataFrame['close']
    VHF_1 = ROLLMAX(CLOSE, period) - ROLLMIN(CLOSE,period)
    VHF_2 = SUM(ABS(CLOSE - REF(CLOSE, 1)), period)
    VHF = VHF_1/VHF_2
    res = pd.DataFrame({'VHF': VHF}, index=DataFrame.index)

    return res


def indicator_MSW(DataFrame, period=14, **kwargs):
    pass


def indicator_PFE(DataFrame, period=14, **kwargs):
    pass


# %% 大盘类指标
"""
大盘指标
利用成分股数据构造指标：
对指数成分股的上涨、下跌股票数量或上涨、下跌股票总成交量进行相减或相除以及取移动平均来构造指标，我们将这类指标称为大盘指标。

涨跌比率 ADR
绝对幅度指标 ABI
新三价率 TBR
腾落指数 ADL
广量冲力指标
指数平滑广量 STIX
"""


def indicator_AD_Issues(DataFrame, period=14, **kwargs):
    all_coin_num = (~DataFrame.isnull()).sum(axis=1)
    up_num = (DataFrame > 0).sum(axis=1)
    down_num = (DataFrame < 0).sum(axis=1)
    AD_Issues = MA((up_num - down_num) / all_coin_num, period)
    res = pd.DataFrame({'AD_Issues': AD_Issues}, index=DataFrame.index)
    return res


def indicator_ABI(DataFrame, **kwargs):
    pass


def indicator_ADIO(DataFrame, **kwargs):
    """
    N=40
    ADIO=MA(UP_STOCK-DOWN_STOCK,N)
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    ADIO 指标是上涨股票个数与下跌股票个数差值的移动平均。
    如果 ADIO 上穿 0 则产生买入信号；如果 ADIO 下穿 0 则产生卖出信号。

    :param DataFrame:
    :return:
    """

    return


def indicator_OBOS(DataFrame, period=14, **kwargs):
    pass


def indicator_MO(DataFrame, period=14, **kwargs):
    pass


def indicator_MSI(DataFrame, period=14, **kwargs):
    pass


def indicator_AD_Line(DataFrame, period=14, **kwargs):
    all_coin_num = (~DataFrame.isnull()).sum(axis=1)
    up_num = (DataFrame > 0).sum(axis=1)
    down_num = (DataFrame < 0).sum(axis=1)
    AD_Line = MA((up_num - down_num).cumsum(), period)
    res = pd.DataFrame({'AD_Line': AD_Line}, index=DataFrame.index)
    return res


def indicator_MCL(DataFrame, **kwargs):
    """
    N1=40
    N2=80
    MCL=EMA(UP_STOCK-DOWN_STOCK,N1)-EMA(UP_STOCK-DOWN_STOCK,N2)
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    MCL 指标为（上涨股票个数-下跌股票个数）的 MACD 值，可以看成MIO 指标的简化版本。
    MCL>0 时市场为牛市；MCL<0 时市场为熊市。
    我们可以利用 MCL 上穿/下穿 0 线来产生买入/卖出的交易信号

    :param DataFrame:
    :return:
    """
    return


def indicator_AD_Ratio(DataFrame, period=14, **kwargs):
    pass


def indicator_BT(DataFrame, period=14, **kwargs):
    """
    N=20
    BT=MA(UP_STOCK,N)/MA(UP_STOCK+DOWN_STOCK,N)*100
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    BT 为上涨股票个数的移动平均值除以股票总个数的移动平均。这个指标可以用来抓住长牛的开端，当新的牛市开始时，个股的普涨使得上涨股票个数的比例会显著上升。
    如果 BT 上穿 55，则产生买入信号；
    如果 BT 下穿 45，则产生卖出信号。
    """

    all_coin_num = (~DataFrame.isnull()).sum(axis=1)
    up_num = (DataFrame > 0).sum(axis=1)
    down_num = (DataFrame < 0).sum(axis=1)
    BT = MA(up_num / all_coin_num, period)
    res = pd.DataFrame({'BT': BT}, index=DataFrame.index)
    return res


def indicator_ADR(DataFrame, **kwargs):
    """
    N=5
    ADR=MA(UP_STOCK/DOWN_STOCK,N)
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    ADR 指标为上涨股票个数与下跌股票个数之比的简单移动平均。
    如果 ADR 上穿 1 则产生买入信号；
    如果 ADR 下穿 1 则产生卖出信号。

    :param DataFrame:
    :return:
    """

    return


def indicator_ADIPO(DataFrame, **kwargs):
    """
    N=40
    ADIPO=MA((UP_STOCK-DOWN_STOCK)/(UP_STOCK+DOWN_STOCK)*100,N)
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    ADIPO 指标为（上涨股票个数-下跌股票个数）占股票总个数比例的移动平均值，可以看作 ADIPO 指标的变形。
    如果 ADIPO 上穿 0 则产生买入信号；
    如果 ADIPO 下穿 0 则产生卖出信号。

    :param DataFrame:
    :return:
    """
    return


def indicator_MIO(DataFrame, **kwargs):
    """
    N1=40
    N2=80
    MIO=EMA((UP_STOCK-DOWN_STOCK)/(UP_STOCK+DOWN_STOCK),N1)-EMA((UP_STOCK-DOWN_STOCK)/(UP_STOCK+DOWN_STOCK),N2)
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    MIO 为（上涨股票个数-下跌股票个数）与总股票个数之比的 MACD值。如果 MIO 上穿 0 则产生买入信号；如果 MIO 下穿 0 则产生卖出信号。

    :param DataFrame:
    :return:
    """

    return


def indicator_STIX(DataFrame, **kwargs):
    """
    N=40
    STIX=EMA(UP_STOCK/(UP_STOCK+DOWN_STOCK)*100,N)
    其中 UP_STOCK 和 DOWN_STOCK 分别为价格上涨/下跌的股票的个数。
    STIX 指标衡量上涨股票个数占总个数的比例。
    我们这里可以将其用作动量指标，如果 STIX 上穿 50 则产生买入信号；如果 STIX 下穿50 则产生卖出信号。

    :param DataFrame:
    :return:
    """

    return


def indicator_NHNL_Ratio(DataFrame, period=14, **kwargs):
    all_high_df = (DataFrame - DataFrame.rolling(period).max())
    all_low_df = (DataFrame.rolling(period).min() - DataFrame)
    all_coin_num = (~DataFrame.isnull()).sum(axis=1)

    filter_data = kwargs.pop('filter_data', pd.DataFrame())
    if not filter_data.empty:
        all_high_df = all_high_df[~filter_data.isnull()]
        all_low_df = all_low_df[~filter_data.isnull()]
        all_coin_num = (~filter_data.isnull()).sum(axis=1)

    newhigh_num = (all_high_df == 0).sum(axis=1)
    newlow_num = (all_low_df == 0).sum(axis=1)
    NHNL_Ratio = MA((newhigh_num - newlow_num)/all_coin_num, period)
    res = pd.DataFrame({'NHNL_Ratio': NHNL_Ratio}, index=DataFrame.index)
    return res


def indicator_NHNL_Diff(DataFrame, period=14, **kwargs):
    pass


def indicator_NHLC(DataFrame, period=14, **kwargs):
    pass


def indicator_ADUV(DataFrame, period=14, **kwargs):
    pass


def indicator_UDR(DataFrame, period=14, **kwargs):
    pass


def indicator_ADVR(DataFrame, **kwargs):
    """
    N=10
    M=10
    ADVOLUME=MA(UP_VOLUME,N)
    DEVOLUME=MA(DOWN_VOLUME,N)
    ADVR=MA(ADVOLUME/DEVOLUME,M)
    其中 UP_VOLUME 和 DOWN_VOLUME 分别表示价格上涨/下跌的指数成分股的总成交量。
    ADVR 指标是上涨成交量的移动平均与下跌成交量的移动平均之比的移动平均。如果 ADVR 上穿 1 则产生买入信号；如果 ADVR 下穿1 则产生卖出信号。

    :param DataFrame:
    :return:
    """

    return


def indicator_ADVPO(DataFrame, **kwargs):
    """
    N=20
    ADVPO=MA((UP_VOLUME-DOWN_VOLUME)/(UP_VOLUME+DOWN_VOLUME)*100,N)
    其中 UP_VOLUME 和 DOWN_VOLUME 分别表示价格上涨/下跌的指数成分股的总成交量。
    ADVPO 可以看成 ADVR 指标的变形，用来衡量（上涨股票成交量下跌股票成交量）占总成交量的比例。如果 ADVPO 上穿 0 则产生买入信号；如果 ADVPO 下穿 0 则产生卖出信号。

    :param DataFrame:
    :return:
    """

    return


def indicator_UDV(DataFrame, period=14, **kwargs):
    pass


def indicator_CVI(DataFrame, **kwargs):
    """
    N=20
    CVI=CUMSUM((UP_VOLUME-DOWN_VOLUME))
    即 CVI=昨天的 CVI+（上涨股票成交量-下跌股票成交量）
    CVIMA=MA(CVI,N)
    CVI 可以用来确认市场趋势。一般会结合 CVI 的 N 天均线来产生交易信号。当 CVI 上穿均线时产生买入信号，当 CVI 下穿均线时产生卖出信号。

    :param DataFrame:
    :return:
    """
    return


def indicator_AIT(DataFrame, period=14, **kwargs):
    pass


def indicator_TRIN(DataFrame, **kwargs):
    """
    N=20
    TRIN=MA((UP_STOCK/DOWN_STOCK)/(UP_VOLUME/DOWN_VOLUME),N)
    TRIN 指标综合考虑了上涨、下跌股票的个数和成交量。TRIN 的值越小（大），则多方（空方）力量越强。
    当 TRIN 下穿 0.8 时，产生买入信号；
    当 TRIN 上穿 1.2 时，产生卖出信号。

    :param DataFrame:
    :return:
    """
    return


def indicator_LBO(DataFrame, period=14, **kwargs):
    pass


def indicator_OLPS(DataFrame, period=14, **kwargs):
    pass


def indicator_OBLI(DataFrame, period=14, **kwargs):
    pass


def indicator_OLSR(DataFrame, period=14, **kwargs):
    pass


# %% 量价类指标
"""
量价指标
通过成交量和股价变动关系分析未来趋势
价量趋势PVT
量价趋势VPT
AD          —          Chaikin A/D Line
ADOSC          —          Chaikin A/D Oscillator
能量潮OBV         —          On Balance Volume
"""


def indicator_OBV(DataFrame, **kwargs):
    """
    能量潮 OBV - On Balance Volume
    OBV 指标把成交量分为正的成交量（价格上升时的成交量）和负的成交量（价格下降时）的成交量。
    OBV 就是分了正负之后的成交量的累计和。
    如果 OBV 和价格的均线一起上涨（下跌），则上涨（下跌）趋势被确认。
    如果 OBV 上升（下降）而价格的均线下降（上升），说明价格可能要反转，可能要开始新的下跌（上涨）行情。
    如果 OBV_HISTOGRAM 上穿 0 则买入，下穿 0 则卖出。
    """

    VOLUME = DataFrame.volume
    CLOSE = DataFrame.close

    VOL = IF(CLOSE > REF(CLOSE, 1), VOLUME, -VOLUME)
    VOL = IF(CLOSE != REF(CLOSE, 1), VOL, 0)
    OBV = CUMSUM(VOL)
    OBV.name = 'OBV'

    # res = talib.OBV(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, DataFrame.volume.values)  # On Balance Volume
    res = pd.DataFrame({'OBV': OBV})

    return res


def indicator_PVT(DataFrame, **kwargs):
    """
    PVT 指标用价格的变化率作为权重求成交量的移动平均。
    PVT 指标与 OBV 指标的思想类似，但与 OBV 指标相比，PVT 考虑了价格不同涨跌幅的影响，而 OBV 只考虑了价格的变化方向。
    我们这里用 PVT短期和长期均线的交叉来产生交易信号。
    如果 PVT_MA1 上穿 PVT_MA2，则产生买入信号；
    如果 PVT_MA1 下穿 PVT_MA2，则产生卖出信号。
    :param DataFrame:
    :return:
    """
    CLOSE = DataFrame.close
    VOL = DataFrame.volume
    PVT = ((CLOSE - REF(CLOSE, 1)) / REF(CLOSE, 1) * VOL)
    res = pd.DataFrame({'PVT': PVT})
    return res


def indicator_VAO(DataFrame, **kwargs):
    """
    VAO 与 PVT 类似，都综合考虑成交量和价格,以价格的变化为权重对成交量进行加权。
    但是 PVT 考虑的是两天的价格变化率，而 VAO 考虑的是日内的价格。
    当 VAO 的短期均线上穿 VAO 的长期均线时，做多；反之做空。

    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    WEIGHTED_VOLUME = VOLUME * (CLOSE - (HIGH + LOW) / 2)
    VAO = CUMSUM(WEIGHTED_VOLUME)
    # VAO_MA1 = MA(VAO, short_period)
    # VAO_MA2 = MA(VAO, long_period)

    res = pd.DataFrame({'VAO': VAO})
    return res


def indicator_WVAD(DataFrame, period=6, **kwargs):
    """
    WVAD 是用价格信息对成交量加权的价量指标，用来比较开盘到收盘期间多空双方的力量。
    WVAD 的构造与 CMF 类似，但是 CMF 的权值用的是 CLV(反映收盘价在最高价、最低价之间的位置)，
    而 WVAD用的是收盘价与开盘价的距离（即蜡烛图的实体部分的长度）占最高价与最低价的距离的比例，且没有再除以成交量之和。
    WVAD 上穿 0 线，代表买方力量强；
    WVAD 下穿 0 线，代表卖方力量强。
    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    OPEN = DataFrame.open
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    WVAD = SUM(((CLOSE - OPEN) / (HIGH - LOW) * VOLUME), period)

    res = pd.DataFrame({'WVAD': WVAD})
    return res


def indicator_AD(DataFrame, **kwargs):
    """
    A/D = 昨日A/D + 多空对比 * 今日成交量
    多空对比 = [（收盘价- 最低价） - （最高价 - 收盘价）] / （最高价 - 最低价)
    """
    # AD = CUMSUM(((CLOSE - LOW) - (HIGH - CLOSE)) * VOLUME / (HIGH- LOW))
    # AD_EMA1 = EMA(AD, N1)
    # AD_EMA2 = EMA(AD, N2)
    # ADOSC = AD_EMA1 - AD_EMA2

    AD = talib.AD(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, DataFrame.volume.values)  # AD - Chaikin A/D Line
    res = pd.DataFrame({'AD': AD}, index=DataFrame.index)
    return res


def indicator_TVI(DataFrame, period=14, **kwargs):
    pass


def indicator_KVO(DataFrame, short_period=6, long_period=12, **kwargs):
    """
    这个技术指标的目的是为了观察短期和长期股票资金的流入和流出的情况。
    它的主要用途是确认股票价格趋势的方向和强度。
    KO 与OBV,VPT 等指标类似，都是用价格对成交量进行加权。
    KO 用的是典型价格的变化（只考虑变化方向，不考虑变化量），
    OBV 用的是收盘价的变化（只考虑变化方向，不考虑变化量），
    VPT 用的是价格的变化率（即考虑方向又考虑变化幅度）。
    如果 KO 上穿 0，则产生买入信号；
    如果 KO 下穿 0，则产生卖出信号。
    """

    if short_period == 1:
        return None

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    TYPICAL = (HIGH + LOW + CLOSE) / 3
    VOLUME = IF(TYPICAL - REF(TYPICAL, 1) >= 0, VOLUME, -VOLUME)
    VOLUME_EMA1 = EMA(VOLUME, short_period)
    VOLUME_EMA2 = EMA(VOLUME, long_period)
    KO = VOLUME_EMA1 - VOLUME_EMA2

    res = pd.DataFrame({'KO': KO})
    return res


def indicator_AD_OSC(DataFrame, short_period=3, long_period=10, **kwargs):
    """
    ADL（收集派发线）指标是成交量的加权累计求和，其中权重为 CLV指标。
    ADL 指标可以与 OBV 指标进行类比。
    不同的是 OBV 指标只根据价格的变化方向把成交量分为正、负成交量再累加，而 ADL 是用 CLV 指标作为权重进行成交量的累加。
    我们知道，CLV 指标衡量收盘价在最低价和最高价之间的位置，CLV>0(<0),则收盘价更靠近最高（低）价。CLV 越靠近 1(-1)，则收盘价越靠近最高（低）价。
    如果当天的 CLV>0，则 ADL 会加上成交量*CLV（收集）；如果当天的CLV<0，则 ADL 会减去成交量*CLV（派发）。
    ADOSC 指标是 ADL（收集派发线）指标的短期移动平均与长期移动平均之差。
    如果 ADOSC 上穿 0，则产生买入信号；如果 ADOSC 下穿 0，则产生卖出信号。
    """

    if short_period == 1:
        return None
    res = talib.ADOSC(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, DataFrame.volume.values, short_period, long_period)  # ADOSC - Chaikin A/D Oscillator
    ADOSC = pd.Series(res, index=DataFrame.index)

    ts = ADOSC
    ts = ts[ts[~ts.isnull()].index[0]:]  # 剔除数据最开始的NAN
    signal = ts_threshold_signal(ts, positive=True, bins=[0])
    signal.name = 'ADOSC'
    res = pd.DataFrame(signal)
    return res


def indicator_CO(DataFrame, short_period=10, long_period=20, **kwargs):
    """
    """
    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    VOL = DataFrame.volume
    MID = CUMSUM(VOL * (2 * CLOSE - HIGH - LOW) / (HIGH + LOW), 0)
    CHO = MA(MID, short_period) - MA(MID, long_period)
    return pd.DataFrame({'CHO': CHO})


def indicator_NVI(DataFrame, **kwargs):
    """
    NVI 是成交量降低的交易日的价格变化百分比的累积。
    NVI 相关理论认为，如果当前价涨量缩，则说明大户主导市场，NVI 可以用来识别价涨量缩的市场（大户主导的市场）。
    如果 NVI 上穿 NVI_MA，则产生买入信号；
    如果 NVI 下穿 NVI_MA，则产生卖出信号。
    :param DataFrame:
    :return:
    """

    # if period == 1:
    #     return None

    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    NVI_INC = IF(VOLUME < REF(VOLUME, 1), 1 + (CLOSE - REF(CLOSE, 1)) / CLOSE, 1)
    NVI_INC[0] = 100
    NVI = CUMPROD(NVI_INC)
    # NVI_MA = MA(NVI, period)

    return pd.DataFrame({'NVI': NVI})


def indicator_PVI(DataFrame, **kwargs):
    """
    PVI 是成交量升高的交易日的价格变化百分比的累积。
    PVI 相关理论认为，如果当前价涨量增，则说明散户主导市场，PVI可以用来识别价涨量增的市场（散户主导的市场）。
    如果 PVI 上穿 PVI_MA，则产生买入信号；
    如果 PVI 下穿 PVI_MA，则产生卖出信号。

    :param DataFrame:
    :return:
    """

    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    PVI_INC = IF(VOLUME > REF(VOLUME, 1), 1 + (CLOSE - REF(CLOSE, 1)) / CLOSE, 1)
    PVI_INC[0] = 100
    PVI = CUMPROD(PVI_INC)
    # PVI_MA = MA(PVI, period)

    res = pd.DataFrame({'PVI': PVI})
    return res


def indicator_CMF(DataFrame, period=6, **kwargs):
    """
    CMF 用 CLV 对成交量进行加权，如果收盘价在高低价的中点之上，
    则为正的成交量（买方力量占优势）；若收盘价在高低价的中点之下，
    则为负的成交量（卖方力量占优势）。
    如果 CMF 上穿 0，则产生买入信号；
    如果 CMF 下穿 0，则产生卖出信号。

    :param DataFrame:
    :param short_period:
    :param long_period:
    :return:
    """

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    CMF = SUM(((CLOSE - LOW) - (HIGH - CLOSE)) * VOLUME / (HIGH - LOW), period) / SUM(VOLUME, period) * 100

    res = pd.DataFrame({"CMF": CMF})
    return res


def indicator_TMF(DataFrame, period=6, **kwargs):
    """
    TMF 指标和 CMF 指标类似，都是用价格对成交量加权。
    但是 CMF指标用 CLV 做权重，而 TMF 指标用的是真实最低价和真实最高价，且取的是移动平均而不是求和。
    如果 TMF 上穿 0，则产生买入信号；
    如果 TMF 下穿 0，则产生卖出信号。


    :param DataFrame:
    :param short_period:
    :param long_period:
    :return:
    """
    if period == 1:
        return None
    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    HIGH_TRUE = MAX(HIGH, REF(CLOSE, 1))
    LOW_TRUE = MIN(LOW, REF(CLOSE, 1))
    TMF = EMA(VOLUME * (2 * CLOSE - HIGH_TRUE - LOW_TRUE) / (HIGH_TRUE - LOW_TRUE), period) / EMA(VOLUME, period) * 100
    res = pd.DataFrame({'TMF': TMF})
    return res


def indicator_MFI(DataFrame, period=14, **kwargs):
    """
    资金流量指标

    计算方法
    典型价格（TP）=当日最高、最低与收盘价均值
    资金流量（MF）=TP×N日内成交量
    若当日MF>昨日MF，则视为正资金流量（PMF）
    若当日MF<昨日MF，则视为负货币流量（NMF）
    MFI=100-[100/(1+PMF/NMF)]
    参数N一般设为14日。

    TYP := (HIGH + LOW + CLOSE)/3;
    V1:=SUM(IF(TYP>REF(TYP,1),TYP*VOL,0),N)/SUM(IF(TYP<REF(TYP,1),TYP*VOL,0),N);
    MFI:100-(100/(1+V1));
    赋值: (最高价 + 最低价 + 收盘价)/3
    V1赋值:如果TYP>1日前的TYP,返回TYP*成交量(手),否则返回0的N日累和/
           如果TYP<1日前的TYP,返回TYP*成交量(手),否则返回0的N日累和
    输出资金流量指标:100-(100/(1+V1))

    应用法则
    超买超卖信号：当MFI>80时为超买，在其回头向下跌破80时，为短线卖出时机。
    当MFI<20时为超卖，当其回头向上突破20时，为短线买进时机。
    当MFI>80，而产生背离现象时，视为卖出信号。
    当MFI<20，而产生背离现象时，视为买进信号。

    """

    # TYPICAL_PRICE = (HIGH + LOW + CLOSE) / 3
    # MF = TYPICAL_PRICE * VOLUME
    # MF_POS = SUM(IF(TYPICAL_PRICE >= REF(TYPICAL_PRICE, 1), MF, 0), N)
    # MF_NEG = SUM(IF(TYPICAL_PRICE <= REF(TYPICAL_PRICE, 1), MF, 0), N)
    # MFI = 100 - 100 / (1 + MF_POS / MF_NEG)

    # # 自己计算
    # C = DataFrame['close']
    # H = DataFrame['high']
    # L = DataFrame['low']
    # VOL = DataFrame['volume']
    # TYP = (C + H + L) / 3
    # V1 = SUM(IF(TYP > REF(TYP, 1), TYP * VOL, 0), N) / SUM(IF(TYP < REF(TYP, 1), TYP * VOL, 0), N)
    # mfi = 100 - (100 / (1 + V1))
    # DICT = {'MFI': mfi}
    # res = pd.DataFrame(DICT)
    if period == 1:
        return None

    # TALIB计算
    res = talib.MFI(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, DataFrame.volume.values, period)  # Money Flow Index
    res = pd.DataFrame({'MFI': res}, index=DataFrame.index)
    return res


def indicator_VR(DataFrame, period=26, **kwargs):
    """
    VR 用过去 N 日股价上升日成交量与下降日成交量的比值来衡量多空力量对比。
    当 VR 小于 70 时，表示市场较为低迷；上穿 70 时表示市场可能有好转；上穿 250 时表示多方力量压倒空方力量。
    当 VR>300时，市场可能过热、买方力量过强，下穿 300 表明市场可能要反转。
    如果 VR 上穿 250，则产生买入信号；
    如果 VR 下穿 300，则产生卖出信号。

    :param DataFrame:
    :param period:
    :return:
    """
    VOLUME = DataFrame.volume
    CLOSE = DataFrame.close
    AV = IF(CLOSE > REF(CLOSE, 1), VOLUME, 0)
    BV = IF(CLOSE < REF(CLOSE, 1), VOLUME, 0)
    CV = IF(CLOSE == REF(CLOSE, 1), VOLUME, 0)
    AVS = SUM(AV, period)
    BVS = SUM(BV, period)
    CVS = SUM(CV, period)
    VR = (AVS + CVS / 2) / (BVS + CVS / 2) * 100

    return pd.DataFrame({'VR': VR})


def indicator_VRAMT(DataFrame, period=6, **kwargs):
    """
    VRAMT 的计算与 VR 指标（Volume Ratio）一样，只是把其中的成交量替换成了成交额。
    如果 VRAMT 上穿 180，则产生买入信号；
    如果 VRAMT 下穿 70，则产生卖出信号。

    :param DataFrame:
    :param short_period:
    :param long_period:
    :return:
    """

    AMOUNT = DataFrame.amount
    CLOSE = DataFrame.close
    AV = IF(CLOSE > REF(CLOSE, 1), AMOUNT, 0)
    BV = IF(CLOSE < REF(CLOSE, 1), AMOUNT, 0)
    CV = IF(CLOSE == REF(CLOSE, 1), AMOUNT, 0)
    AVS = SUM(AV, period)
    BVS = SUM(BV, period)
    CVS = SUM(CV, period)
    VRAMT = (AVS + CVS / 2) / (BVS + CVS / 2) * 100

    res = pd.DataFrame({'VRAMT': VRAMT})
    return res


def indicator_FI(DataFrame, **kwargs):
    """
    FI 用价格的变化来衡量价格的趋势，用成交量大小来衡量趋势的强弱。我们先对 FI 取移动平均，
    当均线上穿 0 线时产生买入信号，下穿 0 线时产生卖出信号。
    """

    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    FI = (CLOSE - REF(CLOSE, 1)) * VOLUME
    # FIMA = EMA(FI, period)

    return pd.DataFrame({'FI': FI})


def indicator_MFI2(DataFrame, **kwargs):
    # 市场便利指数
    HIGH = DataFrame.high
    LOW = DataFrame.low
    VOLUME = DataFrame.volume
    # AMOUNT = DataFrame.amount
    MFI2 = (HIGH - LOW) / VOLUME
    # MFI2 = (HIGH - LOW) / AMOUNT
    res = pd.DataFrame({'MFI2': MFI2})
    return res


def indicator_EMV(DataFrame, **kwargs):
    """
    EMV 综合考虑了成交量和价格（中间价）的变化。
    EMV>0 则多头处于优势，EMV 上升说明买方力量在增大；
    EMV<0 则空头处于优势，EMV 下降说明卖方力量在增大。
    如果 EMV 上穿 0，则产生买入信号；
    如果 EMV 下穿 0，则产生卖出信号
    """

    HIGH = DataFrame.high
    LOW = DataFrame.low
    VOLUME = DataFrame.volume
    # AMOUNT = DataFrame.amount
    MID_PT_MOVE = (HIGH + LOW) / 2 - (REF(HIGH, 1) + REF(LOW, 1)) / 2
    DIVISOR = 1000000  # 其中 VOLUME_DIVISOR 通常取 1000000
    BOX_RATIO = VOLUME / DIVISOR / (HIGH - LOW)
    # BOX_RATIO = AMOUNT / DIVISOR / (HIGH - LOW)
    EMV = MID_PT_MOVE / BOX_RATIO
    res = pd.DataFrame({'EMV': EMV}, index=DataFrame.index)
    return res


def indicator_RSIV(DataFrame, period=6, **kwargs):
    """
    RSIV 的计算方式与 RSI 相同，只是把其中的价格变化 CLOSEREF(CLOSE,1)替换成了成交量 VOLUME。用法与 RSI 类似。
    我们这里将其用作动量指标，上穿 60 买入，下穿 40 卖出。
    :param DataFrame:
    :param short_period:
    :param long_period:
    :return:
    """
    CLOSE = DataFrame.close
    VOLUME = DataFrame.volume
    VOLUP = IF(CLOSE > REF(CLOSE, 1), VOLUME, 0)
    VOLDOWN = IF(CLOSE < REF(CLOSE, 1), VOLUME, 0)
    SUMUP = SUM(VOLUP, period)
    SUMDOWN = SUM(VOLDOWN, period)
    RSIV = 100 * SUMUP / (SUMUP + SUMDOWN)

    res = pd.DataFrame({'RSIV': RSIV})
    return res


# %% 成交量指标
"""
量能指标
通过成交量的大小和变化研判趋势变化
容量指标 VR
量相对强弱 VRSI
能量指标 CR
人气意愿指标 ARBR
成交量标准差 VSTD
"""


def indicator_MAAMT(DataFrame, period=10, **kwargs):
    """
    MAAMT 是成交额的移动平均线。
    当成交额上穿/下穿移动平均线时产生买入/卖出信号。

    :param DataFrame:
    :param period:
    :return:
    """
    AMOUNT = DataFrame.amount
    MAAMT = MA(AMOUNT, period)

    ts = AMOUNT - MAAMT
    return pd.DataFrame({'MAAMT': ts})


def indicator_MAVOL(DataFrame, period, **kwargs):
    """
    MA_VOLU
    """
    VOL = DataFrame['volume']
    return pd.DataFrame({'MA_VOL': MA(VOL, period)})


def indicator_ROCVOL(DataFrame, period=10, **kwargs):
    """
    ROCVOL 是 ROC 的成交量版本。
    如果 ROCVOL 上穿 0 则买入，下穿 0 则卖出。
    """
    VOLUME = DataFrame.volume
    ROCVOL = (VOLUME - REF(VOLUME, period)) / REF(VOLUME, period)

    return pd.DataFrame({'ROCVOL': ROCVOL})


def indicator_ROCAMT(DataFrame, period=10, **kwargs):
    """
    ROCAMT 是 ROC 的成交额版本。
    如果 ROCAMT 上穿 0 则买入，下穿 0 则卖出。
    """
    AMT = DataFrame.amount
    ROCAMT = (AMT - REF(AMT, period)) / REF(AMT, period)

    return pd.DataFrame({'ROCAMT': ROCAMT})


def indicator_SROCVOL(DataFrame, period=10, ma_period=6, **kwargs):
    """
    SROCVOL 与 ROCVOL 类似，但是会先对成交量进行移动平均平滑处理之后再取其变化率。（SROCVOL 是 SROC 的成交量版本。）
    SROCVOL 上穿 0 买入，下穿 0 卖出。

    :param DataFrame:
    :param period:
    :return:
    """
    if period == 1:
        return None

    VOLUME = DataFrame.volume
    EMAP = EMA(VOLUME, period)
    SROCVOL = (EMAP - REF(EMAP, ma_period)) / REF(EMAP, ma_period) * 100

    return pd.DataFrame({'SROCVOL': SROCVOL})


def indicator_VO(DataFrame, short_period=10, long_period=6, **kwargs):
    """
    PVO 用成交量的指数移动平均来反应成交量的变化。
    PVO 上穿 0 线买入；PVO 下穿 0 线卖出。

    :param DataFrame:
    :param period:
    :return:
    """
    if short_period == 1:
        return None

    VOLUME = DataFrame.volume
    PVO = (EMA(VOLUME, short_period) - EMA(VOLUME, long_period)) / EMA(VOLUME, long_period) * 100

    return pd.DataFrame({'PVO': PVO})


def indicator_BIASVOL(DataFrame, period=12, **kwargs):
    """
    BIASVOL 是乖离率 BIAS 指标的成交量版本。
    """
    VOLUME = DataFrame['volume']
    BIASVOL = (VOLUME - MA(VOLUME, period)) / MA(VOLUME, period) * 100
    return pd.DataFrame({'BIASVOL': BIASVOL})


def indicator_BIASAMT(DataFrame, period=12, **kwargs):
    """
    BIASAMT 是乖离率 BIAS 指标的成交量版本。
    """
    AMT = DataFrame['amount']
    BIASAMT = (AMT - MA(AMT, period)) / MA(AMT, period) * 100
    return pd.DataFrame({'BIASAMT': BIASAMT})


def indicator_MACDVOL(DataFrame, short_period=10, long_period=16, ma_period=6, **kwargs):
    """
    MACDVOL 是 MACD 的成交量版本。
    如果 MACDVOL 上穿 SIGNAL，则买入；下穿 SIGNAL 则卖出。
    :param DataFrame:
    :param period:
    :return:
    """
    if short_period == 1:
        return None
    VOLUME = DataFrame.volume
    MACDVOL = EMA(VOLUME, short_period) - EMA(VOLUME, long_period)
    SIGNAL = MA(MACDVOL, ma_period)

    MACDVOL = MACDVOL - SIGNAL
    return pd.DataFrame({'MACDVOL': MACDVOL})


def indicator_VSTD(DataFrame, period=10, **kwargs):
    """
    交易量的波动率
    :param DataFrame:
    :param period:
    :param kwargs:
    :return:
    """

    VOL = DataFrame.volume
    vstd = STD(VOL, period)
    return pd.DataFrame({'VSTD': vstd})


def indicator_VRSI(DataFrame, period=6, **kwargs):
    """
    类似RSI，
    :param DataFrame:
    :param period:
    :param kwargs:
    :return:
    """
    if period == 1:
        return None

    VOL = DataFrame.volume
    VRSI = SMA(MAX(VOL - REF(VOL, 1), 0), period) / SMA(ABS(VOL - REF(VOL, 1)), period) * 100

    return pd.DataFrame({'VRSI': VRSI})


# %% 形态识别指标  Pattern Recognition
"""
通过K线技术形态判断资产走势

CDL2CROWS          —          Two Crows （两只乌鸦）
CDL3BLACKCROWS          —          Three Black Crows （三只乌鸦）
CDL3INSIDE          —          Three Inside Up/Down （三内部上涨和下跌）
CDL3LINESTRIKE          —          Three-Line Strike （三线打击）
CDL3OUTSIDE          —          Three Outside Up/Down （三外部上涨和下跌）
CDL3STARSINSOUTH          —          Three Stars In The South （南方三星）
CDL3WHITESOLDIERS          —          Three Advancing White Soldiers （三白兵）
CDLABANDONEDBABY          —          Abandoned Baby （弃婴形态）
CDLADVANCEBLOCK          —          Advance Block （前方受阻（大敌当前））
CDLBELTHOLD          —          Belt-hold （捉腰带线）
CDLBREAKAWAY          —          Breakaway （脱离）
CDLCLOSINGMARUBOZU          —          Closing Marubozu （收盘缺影线）
CDLCONCEALBABYSWALL          —          Concealing Baby Swallow （藏婴吞没）
CDLCOUNTERATTACK          —          Counterattack （反击线）
CDLDARKCLOUDCOVER          —          Dark Cloud Cover （乌云压顶）
CDLDOJI          —          Doji （十字）
CDLDOJISTAR          —          Doji Star （十字星）
CDLDRAGONFLYDOJI          —          Dragonfly Doji （蜻蜓十字/T形十字）
CDLENGULFING          —          Engulfing Pattern （吞噬模式）
CDLEVENINGDOJISTAR          —          Evening Doji Star （十字暮星）
CDLEVENINGSTAR          —          Evening Star （暮星）
CDLGAPSIDESIDEWHITE          —          Up/Down-gap side-by-side white lines （向上/下跳空并列阳线）
CDLGRAVESTONEDOJI          —          Gravestone Doji （墓碑十字/倒T十字）
CDLHAMMER          —          Hammer （锤头线）
CDLHANGINGMAN          —          Hanging Man （上吊线）
CDLHARAMI          —          Harami Pattern
CDLHARAMICROSS          —          Harami Cross Pattern
CDLHIGHWAVE          —          High-Wave Candle
CDLHIKKAKE          —          Hikkake Pattern （陷阱）
CDLHIKKAKEMOD          —          Modified Hikkake Pattern
CDLHOMINGPIGEON          —          Homing Pigeon （家鸽）
CDLIDENTICAL3CROWS          —          Identical Three Crows
CDLINNECK          —          In-Neck Pattern
CDLINVERTEDHAMMER          —          Inverted Hammer
CDLKICKING          —          Kicking （反冲形态）
CDLKICKINGBYLENGTH          —          Kicking – bull/bear determined by the longer marubozu
CDLLADDERBOTTOM          —          Ladder Bottom
CDLLONGLEGGEDDOJI          —          Long Legged Doji
CDLLONGLINE          —          Long Line Candle
CDLMARUBOZU          —          Marubozu
CDLMATCHINGLOW          —          Matching Low （相同低价）
CDLMATHOLD          —          Mat Hold
CDLMORNINGDOJISTAR          —          Morning Doji Star （十字晨星）
CDLMORNINGSTAR          —          Morning Star （晨星）
CDLONNECK          —          On-Neck Pattern
CDLPIERCING          —          Piercing Pattern （刺透形态）
CDLRICKSHAWMAN          —          Rickshaw Man
CDLRISEFALL3METHODS          —          Rising/Falling Three Methods
CDLSEPARATINGLINES          —          Separating Lines
CDLSHOOTINGSTAR          —          Shooting Star
CDLSHORTLINE          —          Short Line Candle
CDLSPINNINGTOP          —          Spinning Top（纺锤）
CDLSTALLEDPATTERN          —          Stalled Pattern
CDLSTICKSANDWICH          —          Stick Sandwich
CDLTAKURI          —          Takuri (Dragonfly Doji with very long lower shadow)
CDLTASUKIGAP          —          Tasuki Gap
CDLTHRUSTING          —          Thrusting Pattern
CDLTRISTAR          —          Tristar Pattern
CDLUNIQUE3RIVER          —          Unique 3 River
CDLUPSIDEGAP2CROWS          —          Upside Gap Two Crows
CDLXSIDEGAP3METHODS          —          Upside/Downside Gap Three Methods
"""


def indicator_CDL2CROWS(DataFrame, **kwargs):
    res = talib.CDL2CROWS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDL2CROWS': res}, index=DataFrame.index)


def indicator_CDL3BLACKCROWS(DataFrame, **kwargs):
    res = talib.CDL3BLACKCROWS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDL3BLACKCROWS': res}, index=DataFrame.index)


def indicator_CDL3INSIDE(DataFrame, **kwargs):
    res = talib.CDL3INSIDE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDL3INSIDE': res}, index=DataFrame.index)


def indicator_CDL3LINESTRIKE(DataFrame, **kwargs):
    res = talib.CDL3LINESTRIKE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    res = pd.DataFrame({'CDL3LINESTRIKE': res}, index=DataFrame.index)
    return res


def indicator_CDL3OUTSIDE(DataFrame, **kwargs):
    res = talib.CDL3OUTSIDE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDL3OUTSIDE': res}, index=DataFrame.index)


def indicator_CDL3STARSINSOUTH(DataFrame, **kwargs):
    res = talib.CDL3STARSINSOUTH(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDL3STARSINSOUTH': res}, index=DataFrame.index)


def indicator_CDL3WHITESOLDIERS(DataFrame, **kwargs):
    res = talib.CDL3WHITESOLDIERS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDL3WHITESOLDIERS': res}, index=DataFrame.index)


def indicator_CDLABANDONEDBABY(DataFrame, **kwargs):
    res = talib.CDLABANDONEDBABY(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLABANDONEDBABY': res}, index=DataFrame.index)


def indicator_CDLADVANCEBLOCK(DataFrame, **kwargs):
    res = talib.CDLADVANCEBLOCK(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLADVANCEBLOCK': res}, index=DataFrame.index)


def indicator_CDLBELTHOLD(DataFrame, **kwargs):
    res = talib.CDLBELTHOLD(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLBELTHOLD': res}, index=DataFrame.index)


def indicator_CDLBREAKAWAY(DataFrame, **kwargs):
    res = talib.CDLBREAKAWAY(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLBREAKAWAY': res}, index=DataFrame.index)


def indicator_CDLCLOSINGMARUBOZU(DataFrame, **kwargs):
    """Closing Marubozu (Pattern Recognition)"""

    res = talib.CDLCLOSINGMARUBOZU(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLCLOSINGMARUBOZU': res}, index=DataFrame.index)


def indicator_CDLCONCEALBABYSWALL(DataFrame, **kwargs):
    res = talib.CDLCONCEALBABYSWALL(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLCONCEALBABYSWALL': res}, index=DataFrame.index)


def indicator_CDLCOUNTERATTACK(DataFrame, **kwargs):
    res = talib.CDLCOUNTERATTACK(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLCOUNTERATTACK': res}, index=DataFrame.index)


def indicator_CDLDARKCLOUDCOVER(DataFrame, **kwargs):
    res = talib.CDLDARKCLOUDCOVER(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLDARKCLOUDCOVER': res}, index=DataFrame.index)


def indicator_CDLDOJI(DataFrame, **kwargs):
    res = talib.CDLDOJI(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLDOJI': res}, index=DataFrame.index)


def indicator_CDLDOJISTAR(DataFrame, **kwargs):
    res = talib.CDLDOJISTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLDOJISTAR': res}, index=DataFrame.index)


def indicator_CDLDRAGONFLYDOJI(DataFrame, **kwargs):
    res = talib.CDLDRAGONFLYDOJI(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLDRAGONFLYDOJI': res}, index=DataFrame.index)


def indicator_CDLENGULFING(DataFrame, **kwargs):
    res = talib.CDLENGULFING(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLENGULFING': res}, index=DataFrame.index)


def indicator_CDLEVENINGDOJISTAR(DataFrame, **kwargs):
    res = talib.CDLEVENINGDOJISTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLEVENINGDOJISTAR': res}, index=DataFrame.index)


def indicator_CDLEVENINGSTAR(DataFrame, **kwargs):
    res = talib.CDLEVENINGSTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLEVENINGSTAR': res}, index=DataFrame.index)


def indicator_CDLGAPSIDESIDEWHITE(DataFrame, **kwargs):
    res = talib.CDLGAPSIDESIDEWHITE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLGAPSIDESIDEWHITE': res}, index=DataFrame.index)


def indicator_CDLGRAVESTONEDOJI(DataFrame, **kwargs):
    res = talib.CDLGRAVESTONEDOJI(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLGRAVESTONEDOJI': res}, index=DataFrame.index)


def indicator_CDLHAMMER(DataFrame, **kwargs):
    res = talib.CDLHAMMER(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHAMMER': res}, index=DataFrame.index)


def indicator_CDLHANGINGMAN(DataFrame, **kwargs):
    res = talib.CDLHANGINGMAN(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHANGINGMAN': res}, index=DataFrame.index)


def indicator_CDLHARAMI(DataFrame, **kwargs):
    res = talib.CDLHARAMI(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHARAMI': res}, index=DataFrame.index)


def indicator_CDLHARAMICROSS(DataFrame, **kwargs):
    res = talib.CDLHARAMICROSS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHARAMICROSS': res}, index=DataFrame.index)


def indicator_CDLHIGHWAVE(DataFrame, **kwargs):
    res = talib.CDLHIGHWAVE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHIGHWAVE': res}, index=DataFrame.index)


def indicator_CDLHIKKAKE(DataFrame, **kwargs):
    res = talib.CDLHIKKAKE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHIKKAKE': res}, index=DataFrame.index)


def indicator_CDLHIKKAKEMOD(DataFrame, **kwargs):
    res = talib.CDLHIKKAKEMOD(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHIKKAKEMOD': res}, index=DataFrame.index)


def indicator_CDLHOMINGPIGEON(DataFrame, **kwargs):
    res = talib.CDLHOMINGPIGEON(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLHOMINGPIGEON': res}, index=DataFrame.index)


def indicator_CDLIDENTICAL3CROWS(DataFrame, **kwargs):
    res = talib.CDLIDENTICAL3CROWS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLIDENTICAL3CROWS': res}, index=DataFrame.index)


def indicator_CDLINNECK(DataFrame, **kwargs):
    res = talib.CDLINNECK(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLINNECK': res}, index=DataFrame.index)


def indicator_CDLINVERTEDHAMMER(DataFrame, **kwargs):
    res = talib.CDLINVERTEDHAMMER(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLINVERTEDHAMMER': res}, index=DataFrame.index)


def indicator_CDLKICKING(DataFrame, **kwargs):
    res = talib.CDLKICKING(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLKICKING': res}, index=DataFrame.index)


def indicator_CDLKICKINGBYLENGTH(DataFrame, **kwargs):
    res = talib.CDLKICKINGBYLENGTH(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLKICKINGBYLENGTH': res}, index=DataFrame.index)


def indicator_CDLLADDERBOTTOM(DataFrame, **kwargs):
    res = talib.CDLLADDERBOTTOM(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLLADDERBOTTOM': res}, index=DataFrame.index)


def indicator_CDLLONGLEGGEDDOJI(DataFrame, **kwargs):
    res = talib.CDLLONGLEGGEDDOJI(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLLONGLEGGEDDOJI': res}, index=DataFrame.index)


def indicator_CDLLONGLINE(DataFrame, **kwargs):
    res = talib.CDLLONGLINE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLLONGLINE': res}, index=DataFrame.index)


def indicator_CDLMARUBOZU(DataFrame, **kwargs):
    res = talib.CDLMARUBOZU(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLMARUBOZU': res}, index=DataFrame.index)


def indicator_CDLMATCHINGLOW(DataFrame, **kwargs):
    res = talib.CDLMATCHINGLOW(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLMATCHINGLOW': res}, index=DataFrame.index)


def indicator_CDLMATHOLD(DataFrame, **kwargs):
    res = talib.CDLMATHOLD(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLMATHOLD': res}, index=DataFrame.index)


def indicator_CDLMORNINGDOJISTAR(DataFrame, **kwargs):
    res = talib.CDLMORNINGDOJISTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLMORNINGDOJISTAR': res}, index=DataFrame.index)


def indicator_CDLMORNINGSTAR(DataFrame, **kwargs):
    res = talib.CDLMORNINGSTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLMORNINGSTAR': res}, index=DataFrame.index)


def indicator_CDLONNECK(DataFrame, **kwargs):
    res = talib.CDLONNECK(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLONNECK': res}, index=DataFrame.index)


def indicator_CDLPIERCING(DataFrame, **kwargs):
    res = talib.CDLPIERCING(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLPIERCING': res}, index=DataFrame.index)


def indicator_CDLRICKSHAWMAN(DataFrame, **kwargs):
    res = talib.CDLRICKSHAWMAN(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLRICKSHAWMAN': res}, index=DataFrame.index)


def indicator_CDLRISEFALL3METHODS(DataFrame, **kwargs):
    res = talib.CDLRISEFALL3METHODS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLRISEFALL3METHODS': res}, index=DataFrame.index)


def indicator_CDLSEPARATINGLINES(DataFrame, **kwargs):
    res = talib.CDLSEPARATINGLINES(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLSEPARATINGLINES': res}, index=DataFrame.index)


def indicator_CDLSHOOTINGSTAR(DataFrame, **kwargs):
    res = talib.CDLSHOOTINGSTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLSHOOTINGSTAR': res}, index=DataFrame.index)


def indicator_CDLSHORTLINE(DataFrame, **kwargs):
    res = talib.CDLSHORTLINE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLSHORTLINE': res}, index=DataFrame.index)


def indicator_CDLSPINNINGTOP(DataFrame, **kwargs):
    res = talib.CDLSPINNINGTOP(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLSPINNINGTOP': res}, index=DataFrame.index)


def indicator_CDLSTALLEDPATTERN(DataFrame, **kwargs):
    res = talib.CDLSTALLEDPATTERN(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLSTALLEDPATTERN': res}, index=DataFrame.index)


def indicator_CDLSTICKSANDWICH(DataFrame, **kwargs):
    res = talib.CDLSTICKSANDWICH(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLSTICKSANDWICH': res}, index=DataFrame.index)


def indicator_CDLTAKURI(DataFrame, **kwargs):
    res = talib.CDLTAKURI(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLTAKURI': res}, index=DataFrame.index)


def indicator_CDLTASUKIGAP(DataFrame, **kwargs):
    res = talib.CDLTASUKIGAP(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLTASUKIGAP': res}, index=DataFrame.index)


def indicator_CDLTHRUSTING(DataFrame, **kwargs):
    res = talib.CDLTHRUSTING(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLTHRUSTING': res}, index=DataFrame.index)


def indicator_CDLTRISTAR(DataFrame, **kwargs):
    res = talib.CDLTRISTAR(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLTRISTAR': res}, index=DataFrame.index)


def indicator_CDLUNIQUE3RIVER(DataFrame, **kwargs):
    res = talib.CDLUNIQUE3RIVER(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLUNIQUE3RIVER': res}, index=DataFrame.index)


def indicator_CDLUPSIDEGAP2CROWS(DataFrame, **kwargs):
    res = talib.CDLUPSIDEGAP2CROWS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLUPSIDEGAP2CROWS': res}, index=DataFrame.index)


def indicator_CDLXSIDEGAP3METHODS(DataFrame, **kwargs):
    res = talib.CDLXSIDEGAP3METHODS(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)
    return pd.DataFrame({'CDLXSIDEGAP3METHODS': res}, index=DataFrame.index)


# %% 其他类，待归类
"""
周期指标  Cycle Indicators      
"""


def indicator_HT_DCPERIOD(DataFrame, **kwargs):
    res = talib.HT_DCPERIOD(DataFrame.close.values)  # Hilbert Transform – Dominant Cycle Period
    res = pd.DataFrame({'HT_DCPERIOD': res}, index=DataFrame.index)
    return res


def indicator_HT_DCPHASE(DataFrame, **kwargs):
    res = talib.HT_DCPHASE(DataFrame.close.values)  # Hilbert Transform – Dominant Cycle Phase
    res = pd.DataFrame({'HT_DCPHASE': res}, index=DataFrame.index)
    return res


def indicator_HT_PHASOR_inphase(DataFrame, **kwargs):
    inphase, quadrature = talib.HT_PHASOR(DataFrame.close.values)  # Hilbert Transform – Phasor Components
    res = pd.DataFrame({'HT_PHASOR_inphase': inphase}, index=DataFrame.index)
    return res


def indicator_HT_PHASOR_quadrature(DataFrame, **kwargs):
    inphase, quadrature = talib.HT_PHASOR(DataFrame.close.values)
    res = pd.DataFrame({'HT_PHASOR_quadrature': quadrature}, index=DataFrame.index)
    return res


def indicator_HT_SINE_sine(DataFrame, **kwargs):
    sine, leadsine = talib.HT_SINE(DataFrame.close.values)  # Hilbert Transform – SineWave
    res = pd.DataFrame({'HT_SINE_sine': sine * 100}, index=DataFrame.index)
    return res


def indicator_HT_SINE_leadsine(DataFrame, **kwargs):
    sine, leadsine = talib.HT_SINE(DataFrame.close.values)
    res = pd.DataFrame({'HT_SINE_leadsine': leadsine * 100}, index=DataFrame.index)
    return res


def indicator_HT_TRENDLINE(DataFrame, **kwargs):
    res = talib.HT_TRENDLINE(DataFrame.close.values)  # Hilbert Transform - Instantaneous Trendline
    res = pd.DataFrame({'HT_TRENDLINE': res}, index=DataFrame.index)
    return res


def indicator_HT_TRENDMODE(DataFrame, **kwargs):
    res = talib.HT_TRENDMODE(DataFrame.close.values)  # Hilbert Transform – Trend vs Cycle Mode
    res = pd.DataFrame({'HT_TRENDMODE': res}, index=DataFrame.index)
    return res


"""
波动率指标 Volatility Indicators
ATR          —          Average True Range （平均真实波幅）
NATR          —          Normalized Average True Range
TRANGE          —          True Range （真实波幅）
"""


def indicator_ATR(DataFrame, period=14, **kwargs):
    """
    输出TR:(最高价-最低价)和昨收-最高价的绝对值的较大值和昨收-最低价的绝对值的较大值
    输出真实波幅:TR的N日简单移动平均
    算法：今日振幅、今日最高与昨收差价、今日最低与昨收差价中的最大值，为真实波幅，求真实波幅的N日移动平均
    参数：N　天数，一般取14
    """
    if period == 1:
        return None

    # 自己计算
    # CLOSE = DataFrame['close']
    # HIGH = DataFrame['high']
    # LOW = DataFrame['low']
    # TR = MAX(MAX((HIGH - LOW), ABS(REF(CLOSE, 1) - HIGH)), ABS(REF(CLOSE, 1) - LOW))
    # ATR = MA(TR, period)

    # TALIB计算
    res = talib.ATR(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)  # Average True Range
    res = pd.DataFrame({'ATR': res}, index=DataFrame.index)

    return res


def indicator_NATR(DataFrame, period=14, **kwargs):
    """
    归一化波动幅度均值
    :param DataFrame:
    :param period:
    :param kwargs:
    :return:
    """
    res = talib.NATR(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period) * 10  # Normalized Average True Range
    res = pd.DataFrame({'NATR': res}, index=DataFrame.index)
    return res


def indicator_TRANGE(DataFrame, **kwargs):
    """

    :param DataFrame:
    :param kwargs:
    :return:
    """
    res = talib.TRANGE(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)  # True Range
    res = pd.DataFrame({'TRANGE': res}, index=DataFrame.index)
    return res


"""基础指标"""


def indicator_AVGPRICE(DataFrame, **kwargs):
    """
    AVGPRICE:Average Price
    """
    res = talib.AVGPRICE(DataFrame.open.values, DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)  # Average Price
    return pd.DataFrame({'AVGPRICE': res}, index=DataFrame.index)


def indicator_MEDPRICE(DataFrame, **kwargs):
    """
    MEDPRICE:Median Price
    """
    res = talib.MEDPRICE(DataFrame.high.values, DataFrame.low.values)  # Median Price
    return pd.DataFrame({'MEDPRICE': res}, index=DataFrame.index)


def indicator_TYPPRICE(DataFrame, **kwargs):
    """
    TYPPRICE:Typical Price
    """
    res = talib.TYPPRICE(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)  # Typical Price
    return pd.DataFrame({'TYPPRICE': res}, index=DataFrame.index)


def indicator_WCLPRICE(DataFrame, **kwargs):
    """
    WCLPRICE:Weighted Close Price
    """
    res = talib.WCLPRICE(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values)  # Weighted Close Price
    return pd.DataFrame({'WCLPRICE': res}, index=DataFrame.index)


def indicator_MIDPRICE(DataFrame, period=14, **kwargs):
    if period == 1:
        return None
    res = talib.MIDPRICE(DataFrame.high.values, DataFrame.low.values, period)  # MIDPRICE - Midpoint Price over period
    return pd.DataFrame({'MIDPRICE': res}, index=DataFrame.index)


def indicator_MIDPOINT(DataFrame, period=14, **kwargs):
    if period == 1:
        return None
    res = talib.MIDPOINT(DataFrame.close.values, period)  # MIDPOINT - MidPoint over period
    return pd.DataFrame({'MIDPOINT': res}, index=DataFrame.index)


"""
待归类
"""


def indicator_VI(DataFrame, period=14, **kwargs):
    """
    VI 指标可看成 ADX 指标的变形。VI 指标中的 VI+与 VI-与 ADX 中的DI+与 DI-类似。
    不同的是 ADX 中用当前高价与前一天高价的差和当低价与前一天低价的差来衡量价格变化，
    而 VI 指标用当前当前高价与前一天低价和当前低价与前一天高价的差来衡量价格变化。
    当VI+上穿/下穿 VI-时，多/空的信号更强，产生买入/卖出信号。
    :param DataFrame:
    :param period:
    :return:
    """
    if period == 1:
        return None

    HIGH = DataFrame.high
    LOW = DataFrame.low
    CLOSE = DataFrame.close
    TR = MAX(MAX(HIGH - LOW, ABS(HIGH - REF(CLOSE, 1))), ABS(LOW - REF(CLOSE, 1)))
    VMPOS = ABS(HIGH - REF(LOW, 1))
    VMNEG = ABS(LOW - REF(HIGH, 1))
    SUMPOS = SUM(VMPOS, period)
    SUMNEG = SUM(VMNEG, period)
    TRSUM = SUM(TR, period)
    VI_pos = SUMPOS / TRSUM
    VI_neg = SUMNEG / TRSUM
    VI = (VI_pos - VI_neg) * 100
    return pd.DataFrame({'VI': VI}, index=DataFrame.index)


def indicator_MINUS_DI(DataFrame, period=14, **kwargs):
    """
    MINUS_DI下升动向值(与DX相似)
    分析股票价格在涨跌过程中买卖双方力量均衡点的变化情况，即多空双方的力量的变化受价格波动的影响而发生由均衡到失衡的循环过程，从而提供对趋势判断依据的一种技术指标。
    取值范围0-100
    """
    res = talib.MINUS_DI(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)
    res = pd.DataFrame({'MINUS_DI': res}, index=DataFrame.index)
    return res


def indicator_MINUS_DM(DataFrame, period=14, **kwargs):
    """
    MINUS_DM上升动向值（与DX相似）
    分析股票价格在涨跌过程中买卖双方力量均衡点的变化情况，即多空双方的力量的变化受价格波动的影响而发生由均衡到失衡的循环过程，从而提供对趋势判断依据的一种技术指标。
    取值范围0-inf
    """
    res = talib.MINUS_DM(DataFrame.high.values, DataFrame.low.values, period)
    res = pd.DataFrame({'MINUS_DM': res}, index=DataFrame.index)
    return res


def indicator_PLUS_DI(DataFrame, period=14, **kwargs):
    """
    PLUS_DI - Plus方向指示器
    取值范围0-100
    :param DataFrame:
    :param period:
    :param kwargs:
    :return:
    """
    res = talib.PLUS_DI(DataFrame.high.values, DataFrame.low.values, DataFrame.close.values, period)
    res = pd.DataFrame({'PLUS_DI': res}, index=DataFrame.index)
    return res


def indicator_PLUS_DM(DataFrame, period=14, **kwargs):
    """
    PLUS_DM - Plus定向运动
    取值范围0-inf
    :param DataFrame:
    :param period:
    :param kwargs:
    :return:
    """
    res = talib.PLUS_DM(DataFrame.high.values, DataFrame.low.values, period)
    res = pd.DataFrame({'PLUS_DM': res}, index=DataFrame.index)
    return res


def indicator_SI(DataFrame, period=20, **kwargs):
    """
    SI 用价格变化（即两天收盘价之差，昨日收盘与开盘价之差，今日收盘与开盘价之差）的加权平均来反映价格的变化。
    如果 SI 上穿/下穿0 则产生买入/卖出信号。

    """
    if period == 1:
        return None
    OPEN = DataFrame['open']
    HIGH = DataFrame['high']
    LOW = DataFrame['low']
    CLOSE = DataFrame['close']
    A = ABS(HIGH - REF(CLOSE, 1))
    B = ABS(LOW - REF(CLOSE, 1))
    C = ABS(HIGH - REF(LOW, 1))
    D = ABS(REF(CLOSE, 1) - REF(OPEN, 1))

    K = MAX(A, B)
    M = HHV(HIGH - LOW, period)
    R1 = A + 0.5 * B + 0.25 * D
    R2 = B + 0.5 * A + 0.25 * D
    R3 = C + 0.25 * D
    R4 = IF((A >= B) & (A >= C), R1, R2)
    R = IF((C >= A) & (C >= B), R3, R4)
    SI = 50 * (CLOSE - REF(CLOSE, 1) + (REF(CLOSE, 1) - REF(OPEN, 1)) + 0.5 * (CLOSE - OPEN)) / R * K / M

    return pd.DataFrame({'SI': SI})


def indicator_shadow(DataFrame, **kwargs):
    """
    上下影线指标
    """

    def lower_shadow(DataFrame, **kwargs):  # 下影线
        return abs(DataFrame.low - MIN(DataFrame.open, DataFrame.close))

    def upper_shadow(DataFrame, **kwargs):  # 上影线
        return abs(DataFrame.high - MAX(DataFrame.open, DataFrame.close))

    def body_abs(DataFrame, **kwargs):
        return abs(DataFrame.open - DataFrame.close)

    def body(DataFrame, **kwargs):
        return DataFrame.close - DataFrame.open

    def price_pcg(DataFrame, **kwargs):
        return body(DataFrame) / DataFrame.open

    def amplitude(DataFrame, **kwargs):
        return (DataFrame.high - DataFrame.low) / DataFrame.low

    return {
        'LOW': lower_shadow(DataFrame), 'UP': upper_shadow(DataFrame),
        'BODY': body(DataFrame), 'BODY_ABS': body_abs(DataFrame), 'PRICE_PCG': price_pcg(DataFrame)
    }


def indicator_butterfly_flower(DataFrame, period=27, long_period=5, short_period=3, **kwargs):
    """
    情蝶恋花指标的源程序

    胭脂泪: 3*SMA((CLOSE-LLV(LOW,27))/(HHV(HIGH,27)-LLV(LOW,27))*100,5,1)-2*SMA(SMA((CLOSE-LLV(LOW,27))/(HHV(HIGH,27)-LLV(LOW,27))*100,5,1),3,1),circledot,colorf00ff0;
    伤情线:85,colorffffff;
    断情剑:if(cross(85,胭脂泪),85,60),stick,linethick3,color00ff00 ;60,stick,linethick3,color000000 ;
    知意线: 10,coloryellow;
    情意浓:if(cross(胭脂泪,知意线),30,0),stick,linethick3,colorred ;0,stick,linethick3,color000000 ;
    aa:胭脂泪,circledot,colorf00ff0;

    :param DataFrame:
    :param kwargs:
    :return:
    """

    CLOSE = DataFrame['close']
    HIGH = DataFrame['high']
    LOW = DataFrame['low']

    res = 3 * SMA((CLOSE - LLV(LOW, period)) / (HHV(HIGH, period) - LLV(LOW, period)) * 100, long_period, 1) - \
          2 * SMA(SMA((CLOSE - LLV(LOW, period)) / (HHV(HIGH, period) - LLV(LOW, period)) * 100, long_period, 1), short_period, 1)

    res.name = 'butterfly_flower'
    return pd.DataFrame(res)


if __name__ == '__main__':
    import os
    from quant_researcher.quant.project_tool.localize import DATA_DIR
    file_path = os.path.join(DATA_DIR, f'ta_analysis')

    # 获取量价数据
    file_name = os.path.join(DATA_DIR, f'BTC_history_ohlcvm')
    btc_price_data = pd.read_excel(f'{file_name}.xlsx', index_col='end_date')
    btc_price_data = btc_price_data.loc['2014-01-01':, :]
    btc_price_data = btc_price_data.iloc[:-1, :]
    btc_price_data['log_price'] = np.log10(btc_price_data['close'])

    """
    MA, HMA, LMA, PPO, MACD, BIAS,
    POS, BOP, ADTM, TSI, ARBR, PSY, CV,
    KDJ, WR, RSI, ROC, RVI, BOLL, KC, MI,
    AROON, CMO, ADX, R-SQUARED, RWI, VHF,
    OBV, AD, MFI, EMV,
    ROCVOL, BIASVOL,
    AD Issues, AD line, BT, NHNL,
    """

    period_list = [30, 60, 90, 120, 150, 180, 200]
    # # 计算价格类指标, 需要取log
    # for ta_func in [indicator_MA, indicator_HMA, indicator_LMA]:
    #     indicator_name = ta_func.__name__.replace('indicator_', '')
    #     temp_data = btc_price_data.copy()
    #     for period in period_list:
    #         temp_data[f'{indicator_name}{period}'] = np.log10(ta_func(btc_price_data, period=period))
    #     file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
    #     temp_data.to_excel(f'{file_name}.xlsx')

    # 单个期限入参指标
    # for ta_func in [indicator_AROON]:
    for ta_func in [indicator_BIAS,
                    indicator_BOP, indicator_ADTM, indicator_ARBR, indicator_PSY, indicator_CV,
                    indicator_KDJ, indicator_WR, indicator_RSI, indicator_ROC, indicator_BOLL, indicator_KC, indicator_MI,
                    indicator_AROON, indicator_CMO, indicator_ADX, indicator_R_Squared, indicator_RWI_H, indicator_RWI_L, indicator_VHF,
                    indicator_ROCVOL, indicator_BIASVOL, indicator_ROCAMT, indicator_BIASAMT]:
        indicator_name = ta_func.__name__.replace('indicator_', '')
        temp_data = btc_price_data.copy()
        for period in period_list:
            temp_data[f'{indicator_name}{period}'] = ta_func(btc_price_data, period=period)
        file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
        temp_data.to_excel(f'{file_name}.xlsx')

    # # 单个期限与均线入参指标
    # for ta_func in [indicator_POS,
    #                 indicator_RVI]:
    #     indicator_name = ta_func.__name__.replace('indicator_', '')
    #     temp_data = btc_price_data.copy()
    #     for period in period_list:
    #         for ma_period in [10, 20, 30, 60, 90, 120]:
    #             temp_data[f'{indicator_name}{period}{ma_period}'] = ta_func(btc_price_data, period=period, ma_period=ma_period)
    #     file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
    #     temp_data.to_excel(f'{file_name}.xlsx')
    #
    # 长短期指标
    short_period_list = [10, 20, 30, 40, 50, 60]
    long_period_list = [30, 60, 90, 120, 150, 200]
    for ta_func in [indicator_PPO, indicator_PPO_revised,
                    indicator_TSI]:
        indicator_name = ta_func.__name__.replace('indicator_', '')
        temp_data = btc_price_data.copy()
        for period1 in [1, 3, 5, 10, 20, 30, 50, 60, 90]:
            for period2 in [i for i in long_period_list if i > period1]:
                temp_data[f'{indicator_name}{period1}{period2}'] = ta_func(btc_price_data, short_period=period1, long_period=period2)
        file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
        temp_data.to_excel(f'{file_name}.xlsx')

    # file_name = os.path.join(file_path, f'TA_analysis_PPO_revised')
    # temp_data = pd.read_excel(f'{file_name}.xlsx', index_col='end_date')
    # temp_data_1 = temp_data.copy()
    # for selected_column in ['PPO_revised1120', 'PPO_revised3120', 'PPO_revised5120', 'PPO_revised10120']:
    #     temp_data_1.loc[temp_data_1[selected_column] < 0, selected_column] = -1
    #     temp_data_1.loc[temp_data_1[selected_column] > 0, selected_column] = 1
    #     temp_data_1[selected_column] = temp_data_1[selected_column].diff()
    # for selected_column_pair in [['PPO_revised1120', 'PPO_revised3120'], ['PPO_revised1120', 'PPO_revised5120'], ['PPO_revised1120', 'PPO_revised10120']]:
    #     save_data = temp_data.loc[(temp_data_1[selected_column_pair[0]] != 0) | (temp_data_1[selected_column_pair[1]] != 0), ['close', 'log_price', selected_column_pair[0], selected_column_pair[1]]]
    #     save_data = save_data.loc['2015-01-01':, :]
    #     file_name = os.path.join(file_path, f'{selected_column_pair}')
    #     save_data.to_excel(f'{file_name}.xlsx')

    file_name = os.path.join(file_path, f'TA_analysis_PPO_revised')
    temp_data_1 = pd.read_excel(f'{file_name}.xlsx', index_col='end_date')
    file_name = os.path.join(file_path, f'TA_analysis_TSI')
    temp_data_2 = pd.read_excel(f'{file_name}.xlsx', index_col='end_date')
    temp_data = pd.concat([temp_data_1[['close', 'log_price', 'PPO_revised1120']], temp_data_2[['TSI590', 'TSI5120', 'TSI5150']]], axis=1)
    temp_data_1 = temp_data.copy()
    for selected_column in ['PPO_revised1120', 'TSI590', 'TSI5120', 'TSI5150']:
        temp_data_1.loc[temp_data_1[selected_column] < 0, selected_column] = -1
        temp_data_1.loc[temp_data_1[selected_column] > 0, selected_column] = 1
        temp_data_1[selected_column] = temp_data_1[selected_column].diff()
    for selected_column_pair in [['PPO_revised1120', 'TSI590'], ['PPO_revised1120', 'TSI5120'], ['PPO_revised1120', 'TSI5150']]:
        save_data = temp_data.loc[(temp_data_1[selected_column_pair[0]] != 0) | (temp_data_1[selected_column_pair[1]] != 0), ['close', 'log_price', selected_column_pair[0], selected_column_pair[1]]]
        save_data = save_data.loc['2015-01-01':, :]
        file_name = os.path.join(file_path, f'{selected_column_pair}')
        save_data.to_excel(f'{file_name}.xlsx')

    # 长短期与均线比较指标
    for ta_func in [indicator_MACD]:
        indicator_name = ta_func.__name__.replace('indicator_', '')
        temp_data = btc_price_data.copy()
        for period1 in [10, 20, 30, 50, 60, 90]:
            for period2 in [i for i in long_period_list if i > period1]:
                for ma_period in [10, 20, 30, 60, 90, 120]:
                    temp_data[f'{indicator_name}{period1}{period2}{ma_period}'] = ta_func(btc_price_data, short_period=period1, long_period=period2, ma_period=ma_period)
        file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
        temp_data.to_excel(f'{file_name}.xlsx')

    # 无入参指标
    for ta_func in [indicator_OBV, indicator_AD, indicator_MFI2, indicator_EMV]:
        indicator_name = ta_func.__name__.replace('indicator_', '')
        temp_data = btc_price_data.copy()
        temp_data[f'{indicator_name}'] = ta_func(btc_price_data)
        file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
        temp_data.to_excel(f'{file_name}.xlsx')

    # # 统计全市场类技术指标
    # # 获取全市场清洗后的收益率数据
    # temp_file_path = os.path.join(DATA_DIR, f'all_history_ohlcvm_coinmarketcap')
    # file_name = os.path.join(temp_file_path, f'all_history_ohlcvm_daily_ret_cleaned')
    # all_ret_data = pd.read_csv(f'{file_name}.csv', index_col='end_date')
    # all_ret_data = all_ret_data.loc['2015-01-01':, :]
    #
    # # for ta_func in [indicator_AD_Issues, indicator_AD_Line, indicator_BT]:
    # #     indicator_name = ta_func.__name__.replace('indicator_', '')
    # #     temp_data = btc_price_data.copy()
    # #     for period in period_list:
    # #         temp_data[f'{indicator_name}{period}'] = ta_func(all_ret_data, period=period)
    # #     file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
    # #     temp_data.to_excel(f'{file_name}.xlsx')
    #
    # file_name = os.path.join(temp_file_path, f'all_history_ohlcvm_price_cleaned')
    # all_price_data = pd.read_csv(f'{file_name}.csv', index_col='end_date')
    # all_price_data = all_price_data.loc['2015-01-02':, :]
    # for ta_func in [indicator_NHNL_Ratio]:
    #     indicator_name = ta_func.__name__.replace('indicator_', '')
    #     temp_data = btc_price_data.copy()
    #     for period in period_list:
    #         temp_data[f'{indicator_name}{period}'] = ta_func(all_price_data, period=period, filter_data=all_ret_data)
    #     file_name = os.path.join(file_path, f'TA_analysis_{indicator_name}')
    #     temp_data.to_excel(f'{file_name}.xlsx')